highstock.src.js 2.2 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741457424574345744457454574645747457484574945750457514575245753457544575545756457574575845759457604576145762457634576445765457664576745768457694577045771457724577345774457754577645777457784577945780457814578245783457844578545786457874578845789457904579145792457934579445795457964579745798457994580045801458024580345804458054580645807458084580945810458114581245813458144581545816458174581845819458204582145822458234582445825458264582745828458294583045831458324583345834458354583645837458384583945840458414584245843458444584545846458474584845849458504585145852458534585445855458564585745858458594586045861458624586345864458654586645867458684586945870458714587245873458744587545876458774587845879458804588145882458834588445885458864588745888458894589045891458924589345894458954589645897458984589945900459014590245903459044590545906459074590845909459104591145912459134591445915459164591745918459194592045921459224592345924459254592645927459284592945930459314593245933459344593545936459374593845939459404594145942459434594445945459464594745948459494595045951459524595345954459554595645957459584595945960459614596245963459644596545966459674596845969459704597145972459734597445975459764597745978459794598045981459824598345984459854598645987459884598945990459914599245993459944599545996459974599845999460004600146002460034600446005460064600746008460094601046011460124601346014460154601646017460184601946020460214602246023460244602546026460274602846029460304603146032460334603446035460364603746038460394604046041460424604346044460454604646047460484604946050460514605246053460544605546056460574605846059460604606146062460634606446065460664606746068460694607046071460724607346074460754607646077460784607946080460814608246083460844608546086460874608846089460904609146092460934609446095460964609746098460994610046101461024610346104461054610646107461084610946110461114611246113461144611546116461174611846119461204612146122461234612446125461264612746128461294613046131461324613346134461354613646137461384613946140461414614246143461444614546146461474614846149461504615146152461534615446155461564615746158461594616046161461624616346164461654616646167461684616946170461714617246173461744617546176461774617846179461804618146182461834618446185461864618746188461894619046191461924619346194461954619646197461984619946200462014620246203462044620546206462074620846209462104621146212462134621446215462164621746218462194622046221462224622346224462254622646227462284622946230462314623246233462344623546236462374623846239462404624146242462434624446245462464624746248462494625046251462524625346254462554625646257462584625946260462614626246263462644626546266462674626846269462704627146272462734627446275462764627746278462794628046281462824628346284462854628646287462884628946290462914629246293462944629546296462974629846299463004630146302463034630446305463064630746308463094631046311463124631346314463154631646317463184631946320463214632246323463244632546326463274632846329463304633146332463334633446335463364633746338463394634046341463424634346344463454634646347463484634946350463514635246353463544635546356463574635846359463604636146362463634636446365463664636746368463694637046371463724637346374463754637646377463784637946380463814638246383463844638546386463874638846389463904639146392463934639446395463964639746398463994640046401464024640346404464054640646407464084640946410464114641246413464144641546416464174641846419464204642146422464234642446425464264642746428464294643046431464324643346434464354643646437464384643946440464414644246443464444644546446464474644846449464504645146452464534645446455464564645746458464594646046461464624646346464464654646646467464684646946470464714647246473464744647546476464774647846479464804648146482464834648446485464864648746488464894649046491464924649346494464954649646497464984649946500465014650246503465044650546506465074650846509465104651146512465134651446515465164651746518465194652046521465224652346524465254652646527465284652946530465314653246533465344653546536465374653846539465404654146542465434654446545465464654746548465494655046551465524655346554465554655646557465584655946560465614656246563465644656546566465674656846569465704657146572465734657446575465764657746578465794658046581465824658346584465854658646587465884658946590465914659246593465944659546596465974659846599466004660146602466034660446605466064660746608466094661046611466124661346614466154661646617466184661946620466214662246623466244662546626466274662846629466304663146632466334663446635466364663746638466394664046641466424664346644466454664646647466484664946650466514665246653466544665546656466574665846659466604666146662466634666446665466664666746668466694667046671466724667346674466754667646677466784667946680466814668246683466844668546686466874668846689466904669146692466934669446695466964669746698466994670046701467024670346704467054670646707467084670946710467114671246713467144671546716467174671846719467204672146722467234672446725467264672746728467294673046731467324673346734467354673646737467384673946740467414674246743467444674546746467474674846749467504675146752467534675446755467564675746758467594676046761467624676346764467654676646767467684676946770467714677246773467744677546776467774677846779467804678146782467834678446785467864678746788467894679046791467924679346794467954679646797467984679946800468014680246803468044680546806468074680846809468104681146812468134681446815468164681746818468194682046821468224682346824468254682646827468284682946830468314683246833468344683546836468374683846839468404684146842468434684446845468464684746848468494685046851468524685346854468554685646857468584685946860468614686246863468644686546866468674686846869468704687146872468734687446875468764687746878468794688046881468824688346884468854688646887468884688946890468914689246893468944689546896468974689846899469004690146902469034690446905469064690746908469094691046911469124691346914469154691646917469184691946920469214692246923469244692546926469274692846929469304693146932469334693446935469364693746938469394694046941469424694346944469454694646947469484694946950469514695246953469544695546956469574695846959469604696146962469634696446965469664696746968469694697046971469724697346974469754697646977469784697946980469814698246983469844698546986469874698846989469904699146992469934699446995469964699746998469994700047001470024700347004470054700647007470084700947010470114701247013470144701547016470174701847019470204702147022470234702447025470264702747028470294703047031470324703347034470354703647037470384703947040470414704247043470444704547046470474704847049470504705147052470534705447055470564705747058470594706047061470624706347064470654706647067470684706947070470714707247073470744707547076470774707847079470804708147082470834708447085470864708747088470894709047091470924709347094470954709647097470984709947100471014710247103471044710547106471074710847109471104711147112471134711447115471164711747118471194712047121471224712347124471254712647127471284712947130471314713247133471344713547136471374713847139471404714147142471434714447145471464714747148471494715047151471524715347154471554715647157471584715947160471614716247163471644716547166471674716847169471704717147172471734717447175471764717747178471794718047181471824718347184471854718647187471884718947190471914719247193471944719547196471974719847199472004720147202472034720447205472064720747208472094721047211472124721347214472154721647217472184721947220472214722247223472244722547226472274722847229472304723147232472334723447235472364723747238472394724047241472424724347244472454724647247472484724947250472514725247253472544725547256472574725847259472604726147262472634726447265472664726747268472694727047271472724727347274472754727647277472784727947280472814728247283472844728547286472874728847289472904729147292472934729447295472964729747298472994730047301473024730347304473054730647307473084730947310473114731247313473144731547316473174731847319473204732147322473234732447325473264732747328473294733047331473324733347334473354733647337473384733947340473414734247343473444734547346473474734847349473504735147352473534735447355473564735747358473594736047361473624736347364473654736647367473684736947370473714737247373473744737547376473774737847379473804738147382473834738447385473864738747388473894739047391473924739347394473954739647397473984739947400474014740247403474044740547406474074740847409474104741147412474134741447415474164741747418474194742047421474224742347424474254742647427474284742947430474314743247433474344743547436474374743847439474404744147442474434744447445474464744747448474494745047451474524745347454474554745647457474584745947460474614746247463474644746547466474674746847469474704747147472474734747447475474764747747478474794748047481474824748347484474854748647487474884748947490474914749247493474944749547496474974749847499475004750147502475034750447505475064750747508475094751047511475124751347514475154751647517475184751947520475214752247523475244752547526475274752847529475304753147532475334753447535475364753747538475394754047541475424754347544475454754647547475484754947550475514755247553475544755547556475574755847559475604756147562475634756447565475664756747568475694757047571475724757347574475754757647577475784757947580475814758247583475844758547586475874758847589475904759147592475934759447595475964759747598475994760047601476024760347604476054760647607476084760947610476114761247613476144761547616476174761847619476204762147622476234762447625476264762747628476294763047631476324763347634476354763647637476384763947640476414764247643476444764547646476474764847649476504765147652476534765447655476564765747658476594766047661476624766347664476654766647667476684766947670476714767247673476744767547676476774767847679476804768147682476834768447685476864768747688476894769047691476924769347694476954769647697476984769947700477014770247703477044770547706477074770847709477104771147712477134771447715477164771747718477194772047721477224772347724477254772647727477284772947730477314773247733477344773547736477374773847739477404774147742477434774447745477464774747748477494775047751477524775347754477554775647757477584775947760477614776247763477644776547766477674776847769477704777147772477734777447775477764777747778477794778047781477824778347784477854778647787477884778947790477914779247793477944779547796477974779847799478004780147802478034780447805478064780747808478094781047811478124781347814478154781647817478184781947820478214782247823478244782547826478274782847829478304783147832478334783447835478364783747838478394784047841478424784347844478454784647847478484784947850478514785247853478544785547856478574785847859478604786147862478634786447865478664786747868478694787047871478724787347874478754787647877478784787947880478814788247883478844788547886478874788847889478904789147892478934789447895478964789747898478994790047901479024790347904479054790647907479084790947910479114791247913479144791547916479174791847919479204792147922479234792447925479264792747928479294793047931479324793347934479354793647937479384793947940479414794247943479444794547946479474794847949479504795147952479534795447955479564795747958479594796047961479624796347964479654796647967479684796947970479714797247973479744797547976479774797847979479804798147982479834798447985479864798747988479894799047991479924799347994479954799647997479984799948000480014800248003480044800548006480074800848009480104801148012480134801448015480164801748018480194802048021480224802348024480254802648027480284802948030480314803248033480344803548036480374803848039480404804148042480434804448045480464804748048480494805048051480524805348054480554805648057480584805948060480614806248063480644806548066480674806848069480704807148072480734807448075480764807748078480794808048081480824808348084480854808648087480884808948090480914809248093480944809548096480974809848099481004810148102481034810448105481064810748108481094811048111481124811348114481154811648117481184811948120481214812248123481244812548126481274812848129481304813148132481334813448135481364813748138481394814048141481424814348144481454814648147481484814948150481514815248153481544815548156481574815848159481604816148162481634816448165481664816748168481694817048171481724817348174481754817648177481784817948180481814818248183481844818548186481874818848189481904819148192481934819448195481964819748198481994820048201482024820348204482054820648207482084820948210482114821248213482144821548216482174821848219482204822148222482234822448225482264822748228482294823048231482324823348234482354823648237482384823948240482414824248243482444824548246482474824848249482504825148252482534825448255482564825748258482594826048261482624826348264482654826648267482684826948270482714827248273482744827548276482774827848279482804828148282482834828448285482864828748288482894829048291482924829348294482954829648297482984829948300483014830248303483044830548306483074830848309483104831148312483134831448315483164831748318483194832048321483224832348324483254832648327483284832948330483314833248333483344833548336483374833848339483404834148342483434834448345483464834748348483494835048351483524835348354483554835648357483584835948360483614836248363483644836548366483674836848369483704837148372483734837448375483764837748378483794838048381483824838348384483854838648387483884838948390483914839248393483944839548396483974839848399484004840148402484034840448405484064840748408484094841048411484124841348414484154841648417484184841948420484214842248423484244842548426484274842848429484304843148432484334843448435484364843748438484394844048441484424844348444484454844648447484484844948450484514845248453484544845548456484574845848459484604846148462484634846448465484664846748468484694847048471484724847348474484754847648477484784847948480484814848248483484844848548486484874848848489484904849148492484934849448495484964849748498484994850048501485024850348504485054850648507485084850948510485114851248513485144851548516485174851848519485204852148522485234852448525485264852748528485294853048531485324853348534485354853648537485384853948540485414854248543485444854548546485474854848549485504855148552485534855448555485564855748558485594856048561485624856348564485654856648567485684856948570485714857248573485744857548576485774857848579485804858148582485834858448585485864858748588485894859048591485924859348594485954859648597485984859948600486014860248603486044860548606486074860848609486104861148612486134861448615486164861748618486194862048621486224862348624486254862648627486284862948630486314863248633486344863548636486374863848639486404864148642486434864448645486464864748648486494865048651486524865348654486554865648657486584865948660486614866248663486644866548666486674866848669486704867148672486734867448675486764867748678486794868048681486824868348684486854868648687486884868948690486914869248693486944869548696486974869848699487004870148702487034870448705487064870748708487094871048711487124871348714487154871648717487184871948720487214872248723487244872548726487274872848729487304873148732487334873448735487364873748738487394874048741487424874348744487454874648747487484874948750487514875248753487544875548756487574875848759487604876148762487634876448765487664876748768487694877048771487724877348774487754877648777487784877948780487814878248783487844878548786487874878848789487904879148792487934879448795487964879748798487994880048801488024880348804488054880648807488084880948810488114881248813488144881548816488174881848819488204882148822488234882448825488264882748828488294883048831488324883348834488354883648837488384883948840488414884248843488444884548846488474884848849488504885148852488534885448855488564885748858488594886048861488624886348864488654886648867488684886948870488714887248873488744887548876488774887848879488804888148882488834888448885488864888748888488894889048891488924889348894488954889648897488984889948900489014890248903489044890548906489074890848909489104891148912489134891448915489164891748918489194892048921489224892348924489254892648927489284892948930489314893248933489344893548936489374893848939489404894148942489434894448945489464894748948489494895048951489524895348954489554895648957489584895948960489614896248963489644896548966489674896848969489704897148972489734897448975489764897748978489794898048981489824898348984489854898648987489884898948990489914899248993489944899548996489974899848999490004900149002490034900449005490064900749008490094901049011490124901349014490154901649017490184901949020490214902249023490244902549026490274902849029490304903149032490334903449035490364903749038490394904049041490424904349044490454904649047490484904949050490514905249053490544905549056490574905849059490604906149062490634906449065490664906749068490694907049071490724907349074490754907649077490784907949080490814908249083490844908549086
  1. /**
  2. * @license Highstock JS v8.0.0 (2019-12-10)
  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-2019 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.0.0',
  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-2019 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. *
  293. * @interface Highcharts.Dictionary<T>
  294. */ /**
  295. * @name Highcharts.Dictionary<T>#[key:string]
  296. * @type {T}
  297. */
  298. /**
  299. * The function callback to execute when the event is fired. The `this` context
  300. * contains the instance, that fired the event.
  301. *
  302. * @callback Highcharts.EventCallbackFunction<T>
  303. *
  304. * @param {T} this
  305. *
  306. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  307. * Event arguments.
  308. *
  309. * @return {boolean|void}
  310. */
  311. /**
  312. * The event options for adding function callback.
  313. *
  314. * @interface Highcharts.EventOptionsObject
  315. */ /**
  316. * The order the event handler should be called. This opens for having one
  317. * handler be called before another, independent of in which order they were
  318. * added.
  319. * @name Highcharts.EventOptionsObject#order
  320. * @type {number}
  321. */
  322. /**
  323. * Formats data as a string. Usually the data is accessible throught the `this`
  324. * keyword.
  325. *
  326. * @callback Highcharts.FormatterCallbackFunction<T>
  327. *
  328. * @param {T} this
  329. * Context to format
  330. *
  331. * @return {string}
  332. * Formatted text
  333. */
  334. /**
  335. * An object of key-value pairs for HTML attributes.
  336. *
  337. * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
  338. */
  339. /**
  340. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  341. * the global scope.
  342. *
  343. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  344. *
  345. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  346. */
  347. /**
  348. * The iterator callback.
  349. *
  350. * @callback Highcharts.ObjectEachCallbackFunction<T>
  351. *
  352. * @param {T} this
  353. * The context.
  354. *
  355. * @param {*} value
  356. * The property value.
  357. *
  358. * @param {string} key
  359. * The property key.
  360. *
  361. * @param {*} obj
  362. * The object that objectEach is being applied to.
  363. */
  364. /**
  365. * An object containing `left` and `top` properties for the position in the
  366. * page.
  367. *
  368. * @interface Highcharts.OffsetObject
  369. */ /**
  370. * Left distance to the page border.
  371. * @name Highcharts.OffsetObject#left
  372. * @type {number}
  373. */ /**
  374. * Top distance to the page border.
  375. * @name Highcharts.OffsetObject#top
  376. * @type {number}
  377. */
  378. /**
  379. * Describes a range.
  380. *
  381. * @interface Highcharts.RangeObject
  382. */ /**
  383. * Maximum number of the range.
  384. * @name Highcharts.RangeObject#max
  385. * @type {number}
  386. */ /**
  387. * Minimum number of the range.
  388. * @name Highcharts.RangeObject#min
  389. * @type {number}
  390. */
  391. /**
  392. * If a number is given, it defines the pixel length. If a percentage string is
  393. * given, like for example `'50%'`, the setting defines a length relative to a
  394. * base size, for example the size of a container.
  395. *
  396. * @typedef {number|string} Highcharts.RelativeSize
  397. */
  398. /**
  399. * Proceed function to call original (wrapped) function.
  400. *
  401. * @callback Highcharts.WrapProceedFunction
  402. *
  403. * @param {*} [arg1]
  404. * Optional argument. Without any arguments defaults to first argument of
  405. * the wrapping function.
  406. *
  407. * @param {*} [arg2]
  408. * Optional argument. Without any arguments defaults to second argument
  409. * of the wrapping function.
  410. *
  411. * @param {*} [arg3]
  412. * Optional argument. Without any arguments defaults to third argument of
  413. * the wrapping function.
  414. *
  415. * @return {*}
  416. * Return value of the original function.
  417. */
  418. /**
  419. * The Highcharts object is the placeholder for all other members, and various
  420. * utility functions. The most important member of the namespace would be the
  421. * chart constructor.
  422. *
  423. * @example
  424. * var chart = Highcharts.chart('container', { ... });
  425. *
  426. * @namespace Highcharts
  427. */
  428. H.timers = [];
  429. var charts = H.charts, doc = H.doc, win = H.win;
  430. /**
  431. * Provide error messages for debugging, with links to online explanation. This
  432. * function can be overridden to provide custom error handling.
  433. *
  434. * @sample highcharts/chart/highcharts-error/
  435. * Custom error handler
  436. *
  437. * @function Highcharts.error
  438. *
  439. * @param {number|string} code
  440. * The error code. See
  441. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  442. * for available codes. If it is a string, the error message is printed
  443. * directly in the console.
  444. *
  445. * @param {boolean} [stop=false]
  446. * Whether to throw an error or just log a warning in the console.
  447. *
  448. * @param {Highcharts.Chart} [chart]
  449. * Reference to the chart that causes the error. Used in 'debugger'
  450. * module to display errors directly on the chart.
  451. * Important note: This argument is undefined for errors that lack
  452. * access to the Chart instance.
  453. *
  454. * @param {Highcharts.Dictionary<string>} [params]
  455. * Additional parameters for the generated message.
  456. *
  457. * @return {void}
  458. */
  459. H.error = function (code, stop, chart, params) {
  460. var isCode = isNumber(code), message = isCode ?
  461. "Highcharts error #" + code + ": www.highcharts.com/errors/" + code + "/" :
  462. code.toString(), defaultHandler = function () {
  463. if (stop) {
  464. throw new Error(message);
  465. }
  466. // else ...
  467. if (win.console) {
  468. console.log(message); // eslint-disable-line no-console
  469. }
  470. };
  471. if (typeof params !== 'undefined') {
  472. var additionalMessages_1 = '';
  473. if (isCode) {
  474. message += '?';
  475. }
  476. H.objectEach(params, function (value, key) {
  477. additionalMessages_1 += ('\n' + key + ': ' + value);
  478. if (isCode) {
  479. message += encodeURI(key) + '=' + encodeURI(value);
  480. }
  481. });
  482. message += additionalMessages_1;
  483. }
  484. if (chart) {
  485. H.fireEvent(chart, 'displayError', { code: code, message: message, params: params }, defaultHandler);
  486. }
  487. else {
  488. defaultHandler();
  489. }
  490. };
  491. /* eslint-disable no-invalid-this, valid-jsdoc */
  492. /**
  493. * An animator object used internally. One instance applies to one property
  494. * (attribute or style prop) on one element. Animation is always initiated
  495. * through {@link SVGElement#animate}.
  496. *
  497. * @example
  498. * var rect = renderer.rect(0, 0, 10, 10).add();
  499. * rect.animate({ width: 100 });
  500. *
  501. * @private
  502. * @class
  503. * @name Highcharts.Fx
  504. *
  505. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  506. * The element to animate.
  507. *
  508. * @param {Highcharts.AnimationOptionsObject} options
  509. * Animation options.
  510. *
  511. * @param {string} prop
  512. * The single attribute or CSS property to animate.
  513. */
  514. H.Fx = function (elem, options, prop) {
  515. this.options = options;
  516. this.elem = elem;
  517. this.prop = prop;
  518. /* eslint-enable no-invalid-this, valid-jsdoc */
  519. };
  520. H.Fx.prototype = {
  521. /**
  522. * Set the current step of a path definition on SVGElement.
  523. *
  524. * @function Highcharts.Fx#dSetter
  525. *
  526. * @return {void}
  527. */
  528. dSetter: function () {
  529. var start = this.paths[0], end = this.paths[1], ret = [], now = this.now, i = start.length, startVal;
  530. // Land on the final path without adjustment points appended in the ends
  531. if (now === 1) {
  532. ret = this.toD;
  533. }
  534. else if (i === end.length && now < 1) {
  535. while (i--) {
  536. startVal = parseFloat(start[i]);
  537. ret[i] = (
  538. // A letter instruction like M or L
  539. isNaN(startVal) ||
  540. // Arc boolean flags:
  541. end[i - 4] === 'A' || // large-arc-flag
  542. end[i - 5] === 'A' // sweep-flag
  543. ) ?
  544. end[i] :
  545. (now *
  546. parseFloat('' + (end[i] - startVal)) +
  547. startVal);
  548. }
  549. // If animation is finished or length not matching, land on right value
  550. }
  551. else {
  552. ret = end;
  553. }
  554. this.elem.attr('d', ret, null, true);
  555. },
  556. /**
  557. * Update the element with the current animation step.
  558. *
  559. * @function Highcharts.Fx#update
  560. *
  561. * @return {void}
  562. */
  563. update: function () {
  564. var elem = this.elem, prop = this.prop, // if destroyed, it is null
  565. now = this.now, step = this.options.step;
  566. // Animation setter defined from outside
  567. if (this[prop + 'Setter']) {
  568. this[prop + 'Setter']();
  569. // Other animations on SVGElement
  570. }
  571. else if (elem.attr) {
  572. if (elem.element) {
  573. elem.attr(prop, now, null, true);
  574. }
  575. // HTML styles, raw HTML content like container size
  576. }
  577. else {
  578. elem.style[prop] = now + this.unit;
  579. }
  580. if (step) {
  581. step.call(elem, now, this);
  582. }
  583. },
  584. /**
  585. * Run an animation.
  586. *
  587. * @function Highcharts.Fx#run
  588. *
  589. * @param {number} from
  590. * The current value, value to start from.
  591. *
  592. * @param {number} to
  593. * The end value, value to land on.
  594. *
  595. * @param {string} unit
  596. * The property unit, for example `px`.
  597. *
  598. * @return {void}
  599. */
  600. run: function (from, to, unit) {
  601. var self = this, options = self.options, timer = function (gotoEnd) {
  602. return timer.stopped ? false : self.step(gotoEnd);
  603. }, requestAnimationFrame = win.requestAnimationFrame ||
  604. function (step) {
  605. setTimeout(step, 13);
  606. }, step = function () {
  607. for (var i = 0; i < H.timers.length; i++) {
  608. if (!H.timers[i]()) {
  609. H.timers.splice(i--, 1);
  610. }
  611. }
  612. if (H.timers.length) {
  613. requestAnimationFrame(step);
  614. }
  615. };
  616. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  617. delete options.curAnim[this.prop];
  618. if (options.complete && Object.keys(options.curAnim).length === 0) {
  619. options.complete.call(this.elem);
  620. }
  621. }
  622. else { // #7166
  623. this.startTime = +new Date();
  624. this.start = from;
  625. this.end = to;
  626. this.unit = unit;
  627. this.now = this.start;
  628. this.pos = 0;
  629. timer.elem = this.elem;
  630. timer.prop = this.prop;
  631. if (timer() && H.timers.push(timer) === 1) {
  632. requestAnimationFrame(step);
  633. }
  634. }
  635. },
  636. /**
  637. * Run a single step in the animation.
  638. *
  639. * @function Highcharts.Fx#step
  640. *
  641. * @param {boolean} [gotoEnd]
  642. * Whether to go to the endpoint of the animation after abort.
  643. *
  644. * @return {boolean}
  645. * Returns `true` if animation continues.
  646. */
  647. step: function (gotoEnd) {
  648. var t = +new Date(), ret, done, options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim;
  649. if (elem.attr && !elem.element) { // #2616, element is destroyed
  650. ret = false;
  651. }
  652. else if (gotoEnd || t >= duration + this.startTime) {
  653. this.now = this.end;
  654. this.pos = 1;
  655. this.update();
  656. curAnim[this.prop] = true;
  657. done = true;
  658. objectEach(curAnim, function (val) {
  659. if (val !== true) {
  660. done = false;
  661. }
  662. });
  663. if (done && complete) {
  664. complete.call(elem);
  665. }
  666. ret = false;
  667. }
  668. else {
  669. this.pos = options.easing((t - this.startTime) / duration);
  670. this.now = this.start + ((this.end - this.start) * this.pos);
  671. this.update();
  672. ret = true;
  673. }
  674. return ret;
  675. },
  676. /**
  677. * Prepare start and end values so that the path can be animated one to one.
  678. *
  679. * @function Highcharts.Fx#initPath
  680. *
  681. * @param {Highcharts.SVGElement} elem
  682. * The SVGElement item.
  683. *
  684. * @param {string} fromD
  685. * Starting path definition.
  686. *
  687. * @param {Highcharts.SVGPathArray} toD
  688. * Ending path definition.
  689. *
  690. * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
  691. * An array containing start and end paths in array form so that
  692. * they can be animated in parallel.
  693. */
  694. initPath: function (elem, fromD, toD) {
  695. fromD = fromD || '';
  696. var shift, startX = elem.startX, endX = elem.endX, bezier = fromD.indexOf('C') > -1, numParams = bezier ? 7 : 3, fullLength, slice, i, start = fromD.split(' '), end = toD.slice(), // copy
  697. isArea = elem.isArea, positionFactor = isArea ? 2 : 1, reverse;
  698. /**
  699. * In splines make moveTo and lineTo points have six parameters like
  700. * bezier curves, to allow animation one-to-one.
  701. * @private
  702. * @param {Highcharts.SVGPathArray} arr - array
  703. * @return {void}
  704. */
  705. function sixify(arr) {
  706. var isOperator, nextIsOperator;
  707. i = arr.length;
  708. while (i--) {
  709. // Fill in dummy coordinates only if the next operator comes
  710. // three places behind (#5788)
  711. isOperator = arr[i] === 'M' || arr[i] === 'L';
  712. nextIsOperator = /[a-zA-Z]/.test(arr[i + 3]);
  713. if (isOperator && nextIsOperator) {
  714. arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]);
  715. }
  716. }
  717. }
  718. /**
  719. * Insert an array at the given position of another array
  720. * @private
  721. * @param {Array<*>} arr - array
  722. * @param {Array<*>} subArr - array
  723. * @param {number} index - number
  724. * @return {void}
  725. */
  726. function insertSlice(arr, subArr, index) {
  727. [].splice.apply(arr, [index, 0].concat(subArr));
  728. }
  729. /**
  730. * If shifting points, prepend a dummy point to the end path.
  731. * @private
  732. * @param {Highcharts.SVGPathArray} arr - array
  733. * @param {Highcharts.SVGPathArray} other - array
  734. * @return {void}
  735. */
  736. function prepend(arr, other) {
  737. while (arr.length < fullLength) {
  738. // Move to, line to or curve to?
  739. arr[0] = other[fullLength - arr.length];
  740. // Prepend a copy of the first point
  741. insertSlice(arr, arr.slice(0, numParams), 0);
  742. // For areas, the bottom path goes back again to the left, so we
  743. // need to append a copy of the last point.
  744. if (isArea) {
  745. insertSlice(arr, arr.slice(arr.length - numParams), arr.length);
  746. i--;
  747. }
  748. }
  749. arr[0] = 'M';
  750. }
  751. /**
  752. * Copy and append last point until the length matches the end length.
  753. * @private
  754. * @param {Highcharts.SVGPathArray} arr - array
  755. * @param {Highcharts.SVGPathArray} other - array
  756. * @return {void}
  757. */
  758. function append(arr, other) {
  759. var i = (fullLength - arr.length) / numParams;
  760. while (i > 0 && i--) {
  761. // Pull out the slice that is going to be appended or inserted.
  762. // In a line graph, the positionFactor is 1, and the last point
  763. // is sliced out. In an area graph, the positionFactor is 2,
  764. // causing the middle two points to be sliced out, since an area
  765. // path starts at left, follows the upper path then turns and
  766. // follows the bottom back.
  767. slice = arr.slice().splice((arr.length / positionFactor) - numParams, numParams * positionFactor);
  768. // Move to, line to or curve to?
  769. slice[0] = other[fullLength - numParams - (i * numParams)];
  770. // Disable first control point
  771. if (bezier) {
  772. slice[numParams - 6] = slice[numParams - 2];
  773. slice[numParams - 5] = slice[numParams - 1];
  774. }
  775. // Now insert the slice, either in the middle (for areas) or at
  776. // the end (for lines)
  777. insertSlice(arr, slice, arr.length / positionFactor);
  778. if (isArea) {
  779. i--;
  780. }
  781. }
  782. }
  783. if (bezier) {
  784. sixify(start);
  785. sixify(end);
  786. }
  787. // For sideways animation, find out how much we need to shift to get the
  788. // start path Xs to match the end path Xs.
  789. if (startX && endX) {
  790. for (i = 0; i < startX.length; i++) {
  791. // Moving left, new points coming in on right
  792. if (startX[i] === endX[0]) {
  793. shift = i;
  794. break;
  795. // Moving right
  796. }
  797. else if (startX[0] ===
  798. endX[endX.length - startX.length + i]) {
  799. shift = i;
  800. reverse = true;
  801. break;
  802. // Fixed from the right side, "scaling" left
  803. }
  804. else if (startX[startX.length - 1] ===
  805. endX[endX.length - startX.length + i]) {
  806. shift = startX.length - i;
  807. break;
  808. }
  809. }
  810. if (typeof shift === 'undefined') {
  811. start = [];
  812. }
  813. }
  814. if (start.length && isNumber(shift)) {
  815. // The common target length for the start and end array, where both
  816. // arrays are padded in opposite ends
  817. fullLength = (end.length + shift * positionFactor * numParams);
  818. if (!reverse) {
  819. prepend(end, start);
  820. append(start, end);
  821. }
  822. else {
  823. prepend(start, end);
  824. append(end, start);
  825. }
  826. }
  827. return [start, end];
  828. },
  829. /**
  830. * Handle animation of the color attributes directly.
  831. *
  832. * @function Highcharts.Fx#fillSetter
  833. *
  834. * @return {void}
  835. */
  836. fillSetter: function () {
  837. H.Fx.prototype.strokeSetter.apply(this, arguments);
  838. },
  839. /**
  840. * Handle animation of the color attributes directly.
  841. *
  842. * @function Highcharts.Fx#strokeSetter
  843. *
  844. * @return {void}
  845. */
  846. strokeSetter: function () {
  847. this.elem.attr(this.prop, H.color(this.start).tweenTo(H.color(this.end), this.pos), null, true);
  848. }
  849. }; // End of Fx prototype
  850. /* eslint-disable valid-jsdoc */
  851. /**
  852. * Utility function to deep merge two or more objects and return a third object.
  853. * If the first argument is true, the contents of the second object is copied
  854. * into the first object. The merge function can also be used with a single
  855. * object argument to create a deep copy of an object.
  856. *
  857. * @function Highcharts.merge<T>
  858. *
  859. * @param {boolean} extend
  860. * Whether to extend the left-side object (a) or return a whole new
  861. * object.
  862. *
  863. * @param {T|undefined} a
  864. * The first object to extend. When only this is given, the function
  865. * returns a deep copy.
  866. *
  867. * @param {...Array<object|undefined>} [n]
  868. * An object to merge into the previous one.
  869. *
  870. * @return {T}
  871. * The merged object. If the first argument is true, the return is the
  872. * same as the second argument.
  873. */ /**
  874. * Utility function to deep merge two or more objects and return a third object.
  875. * The merge function can also be used with a single object argument to create a
  876. * deep copy of an object.
  877. *
  878. * @function Highcharts.merge<T>
  879. *
  880. * @param {T|undefined} a
  881. * The first object to extend. When only this is given, the function
  882. * returns a deep copy.
  883. *
  884. * @param {...Array<object|undefined>} [n]
  885. * An object to merge into the previous one.
  886. *
  887. * @return {T}
  888. * The merged object. If the first argument is true, the return is the
  889. * same as the second argument.
  890. */
  891. H.merge = function () {
  892. /* eslint-enable valid-jsdoc */
  893. var i, args = arguments, len, ret = {}, doCopy = function (copy, original) {
  894. // An object is replacing a primitive
  895. if (typeof copy !== 'object') {
  896. copy = {};
  897. }
  898. objectEach(original, function (value, key) {
  899. // Copy the contents of objects, but not arrays or DOM nodes
  900. if (isObject(value, true) &&
  901. !isClass(value) &&
  902. !isDOMElement(value)) {
  903. copy[key] = doCopy(copy[key] || {}, value);
  904. // Primitives and arrays are copied over directly
  905. }
  906. else {
  907. copy[key] = original[key];
  908. }
  909. });
  910. return copy;
  911. };
  912. // If first argument is true, copy into the existing object. Used in
  913. // setOptions.
  914. if (args[0] === true) {
  915. ret = args[1];
  916. args = Array.prototype.slice.call(args, 2);
  917. }
  918. // For each argument, extend the return
  919. len = args.length;
  920. for (i = 0; i < len; i++) {
  921. ret = doCopy(ret, args[i]);
  922. }
  923. return ret;
  924. };
  925. /**
  926. * Constrain a value to within a lower and upper threshold.
  927. *
  928. * @private
  929. * @param {number} value The initial value
  930. * @param {number} min The lower threshold
  931. * @param {number} max The upper threshold
  932. * @return {number} Returns a number value within min and max.
  933. */
  934. function clamp(value, min, max) {
  935. return value > min ? value < max ? value : max : min;
  936. }
  937. /**
  938. * Shortcut for parseInt
  939. *
  940. * @private
  941. * @function Highcharts.pInt
  942. *
  943. * @param {*} s
  944. * any
  945. *
  946. * @param {number} [mag]
  947. * Magnitude
  948. *
  949. * @return {number}
  950. * number
  951. */
  952. function pInt(s, mag) {
  953. return parseInt(s, mag || 10);
  954. }
  955. /**
  956. * Utility function to check for string type.
  957. *
  958. * @function Highcharts.isString
  959. *
  960. * @param {*} s
  961. * The item to check.
  962. *
  963. * @return {boolean}
  964. * True if the argument is a string.
  965. */
  966. function isString(s) {
  967. return typeof s === 'string';
  968. }
  969. /**
  970. * Utility function to check if an item is an array.
  971. *
  972. * @function Highcharts.isArray
  973. *
  974. * @param {*} obj
  975. * The item to check.
  976. *
  977. * @return {boolean}
  978. * True if the argument is an array.
  979. */
  980. function isArray(obj) {
  981. var str = Object.prototype.toString.call(obj);
  982. return str === '[object Array]' || str === '[object Array Iterator]';
  983. }
  984. /**
  985. * Utility function to check if an item is of type object.
  986. *
  987. * @function Highcharts.isObject
  988. *
  989. * @param {*} obj
  990. * The item to check.
  991. *
  992. * @param {boolean} [strict=false]
  993. * Also checks that the object is not an array.
  994. *
  995. * @return {boolean}
  996. * True if the argument is an object.
  997. */
  998. function isObject(obj, strict) {
  999. return (!!obj &&
  1000. typeof obj === 'object' &&
  1001. (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
  1002. }
  1003. /**
  1004. * Utility function to check if an Object is a HTML Element.
  1005. *
  1006. * @function Highcharts.isDOMElement
  1007. *
  1008. * @param {*} obj
  1009. * The item to check.
  1010. *
  1011. * @return {boolean}
  1012. * True if the argument is a HTML Element.
  1013. */
  1014. function isDOMElement(obj) {
  1015. return isObject(obj) && typeof obj.nodeType === 'number';
  1016. }
  1017. /**
  1018. * Utility function to check if an Object is a class.
  1019. *
  1020. * @function Highcharts.isClass
  1021. *
  1022. * @param {object|undefined} obj
  1023. * The item to check.
  1024. *
  1025. * @return {boolean}
  1026. * True if the argument is a class.
  1027. */
  1028. function isClass(obj) {
  1029. var c = obj && obj.constructor;
  1030. return !!(isObject(obj, true) &&
  1031. !isDOMElement(obj) &&
  1032. (c && c.name && c.name !== 'Object'));
  1033. }
  1034. /**
  1035. * Utility function to check if an item is a number and it is finite (not NaN,
  1036. * Infinity or -Infinity).
  1037. *
  1038. * @function Highcharts.isNumber
  1039. *
  1040. * @param {*} n
  1041. * The item to check.
  1042. *
  1043. * @return {boolean}
  1044. * True if the item is a finite number
  1045. */
  1046. function isNumber(n) {
  1047. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  1048. }
  1049. /**
  1050. * Remove the last occurence of an item from an array.
  1051. *
  1052. * @function Highcharts.erase
  1053. *
  1054. * @param {Array<*>} arr
  1055. * The array.
  1056. *
  1057. * @param {*} item
  1058. * The item to remove.
  1059. *
  1060. * @return {void}
  1061. */
  1062. function erase(arr, item) {
  1063. var i = arr.length;
  1064. while (i--) {
  1065. if (arr[i] === item) {
  1066. arr.splice(i, 1);
  1067. break;
  1068. }
  1069. }
  1070. }
  1071. /**
  1072. * Check if an object is null or undefined.
  1073. *
  1074. * @function Highcharts.defined
  1075. *
  1076. * @param {*} obj
  1077. * The object to check.
  1078. *
  1079. * @return {boolean}
  1080. * False if the object is null or undefined, otherwise true.
  1081. */
  1082. function defined(obj) {
  1083. return typeof obj !== 'undefined' && obj !== null;
  1084. }
  1085. /**
  1086. * Set or get an attribute or an object of attributes. To use as a setter, pass
  1087. * a key and a value, or let the second argument be a collection of keys and
  1088. * values. To use as a getter, pass only a string as the second argument.
  1089. *
  1090. * @function Highcharts.attr
  1091. *
  1092. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  1093. * The DOM element to receive the attribute(s).
  1094. *
  1095. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [prop]
  1096. * The property or an object of key-value pairs.
  1097. *
  1098. * @param {number|string} [value]
  1099. * The value if a single property is set.
  1100. *
  1101. * @return {string|null|undefined}
  1102. * When used as a getter, return the value.
  1103. */
  1104. function attr(elem, prop, value) {
  1105. var ret;
  1106. // if the prop is a string
  1107. if (isString(prop)) {
  1108. // set the value
  1109. if (defined(value)) {
  1110. elem.setAttribute(prop, value);
  1111. // get the value
  1112. }
  1113. else if (elem && elem.getAttribute) {
  1114. ret = elem.getAttribute(prop);
  1115. // IE7 and below cannot get class through getAttribute (#7850)
  1116. if (!ret && prop === 'class') {
  1117. ret = elem.getAttribute(prop + 'Name');
  1118. }
  1119. }
  1120. // else if prop is defined, it is a hash of key/value pairs
  1121. }
  1122. else {
  1123. objectEach(prop, function (val, key) {
  1124. elem.setAttribute(key, val);
  1125. });
  1126. }
  1127. return ret;
  1128. }
  1129. /**
  1130. * Check if an element is an array, and if not, make it into an array.
  1131. *
  1132. * @function Highcharts.splat
  1133. *
  1134. * @param {*} obj
  1135. * The object to splat.
  1136. *
  1137. * @return {Array}
  1138. * The produced or original array.
  1139. */
  1140. function splat(obj) {
  1141. return isArray(obj) ? obj : [obj];
  1142. }
  1143. /**
  1144. * Set a timeout if the delay is given, otherwise perform the function
  1145. * synchronously.
  1146. *
  1147. * @function Highcharts.syncTimeout
  1148. *
  1149. * @param {Function} fn
  1150. * The function callback.
  1151. *
  1152. * @param {number} delay
  1153. * Delay in milliseconds.
  1154. *
  1155. * @param {*} [context]
  1156. * An optional context to send to the function callback.
  1157. *
  1158. * @return {number}
  1159. * An identifier for the timeout that can later be cleared with
  1160. * Highcharts.clearTimeout. Returns -1 if there is no timeout.
  1161. */
  1162. function syncTimeout(fn, delay, context) {
  1163. if (delay > 0) {
  1164. return setTimeout(fn, delay, context);
  1165. }
  1166. fn.call(0, context);
  1167. return -1;
  1168. }
  1169. /**
  1170. * Internal clear timeout. The function checks that the `id` was not removed
  1171. * (e.g. by `chart.destroy()`). For the details see
  1172. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  1173. *
  1174. * @function Highcharts.clearTimeout
  1175. *
  1176. * @param {number} id
  1177. * Id of a timeout.
  1178. *
  1179. * @return {void}
  1180. */
  1181. H.clearTimeout = function (id) {
  1182. if (defined(id)) {
  1183. clearTimeout(id);
  1184. }
  1185. };
  1186. /* eslint-disable valid-jsdoc */
  1187. /**
  1188. * Utility function to extend an object with the members of another.
  1189. *
  1190. * @function Highcharts.extend<T>
  1191. *
  1192. * @param {T|undefined} a
  1193. * The object to be extended.
  1194. *
  1195. * @param {object} b
  1196. * The object to add to the first one.
  1197. *
  1198. * @return {T}
  1199. * Object a, the original object.
  1200. */
  1201. function extend(a, b) {
  1202. /* eslint-enable valid-jsdoc */
  1203. var n;
  1204. if (!a) {
  1205. a = {};
  1206. }
  1207. for (n in b) { // eslint-disable-line guard-for-in
  1208. a[n] = b[n];
  1209. }
  1210. return a;
  1211. }
  1212. /* eslint-disable valid-jsdoc */
  1213. /**
  1214. * Return the first value that is not null or undefined.
  1215. *
  1216. * @function Highcharts.pick<T>
  1217. *
  1218. * @param {...Array<T|null|undefined>} items
  1219. * Variable number of arguments to inspect.
  1220. *
  1221. * @return {T}
  1222. * The value of the first argument that is not null or undefined.
  1223. */
  1224. function pick() {
  1225. var args = arguments;
  1226. var length = args.length;
  1227. for (var i = 0; i < length; i++) {
  1228. var arg = args[i];
  1229. if (typeof arg !== 'undefined' && arg !== null) {
  1230. return arg;
  1231. }
  1232. }
  1233. }
  1234. /**
  1235. * Set CSS on a given element.
  1236. *
  1237. * @function Highcharts.css
  1238. *
  1239. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
  1240. * An HTML DOM element.
  1241. *
  1242. * @param {Highcharts.CSSObject} styles
  1243. * Style object with camel case property names.
  1244. *
  1245. * @return {void}
  1246. */
  1247. H.css = function (el, styles) {
  1248. if (H.isMS && !H.svg) { // #2686
  1249. if (styles && typeof styles.opacity !== 'undefined') {
  1250. styles.filter =
  1251. 'alpha(opacity=' + (styles.opacity * 100) + ')';
  1252. }
  1253. }
  1254. extend(el.style, styles);
  1255. };
  1256. /**
  1257. * Utility function to create an HTML element with attributes and styles.
  1258. *
  1259. * @function Highcharts.createElement
  1260. *
  1261. * @param {string} tag
  1262. * The HTML tag.
  1263. *
  1264. * @param {Highcharts.HTMLAttributes} [attribs]
  1265. * Attributes as an object of key-value pairs.
  1266. *
  1267. * @param {Highcharts.CSSObject} [styles]
  1268. * Styles as an object of key-value pairs.
  1269. *
  1270. * @param {Highcharts.HTMLDOMElement} [parent]
  1271. * The parent HTML object.
  1272. *
  1273. * @param {boolean} [nopad=false]
  1274. * If true, remove all padding, border and margin.
  1275. *
  1276. * @return {Highcharts.HTMLDOMElement}
  1277. * The created DOM element.
  1278. */
  1279. H.createElement = function (tag, attribs, styles, parent, nopad) {
  1280. var el = doc.createElement(tag), css = H.css;
  1281. if (attribs) {
  1282. extend(el, attribs);
  1283. }
  1284. if (nopad) {
  1285. css(el, { padding: '0', border: 'none', margin: '0' });
  1286. }
  1287. if (styles) {
  1288. css(el, styles);
  1289. }
  1290. if (parent) {
  1291. parent.appendChild(el);
  1292. }
  1293. return el;
  1294. };
  1295. // eslint-disable-next-line valid-jsdoc
  1296. /**
  1297. * Extend a prototyped class by new members.
  1298. *
  1299. * @function Highcharts.extendClass<T>
  1300. *
  1301. * @param {Highcharts.Class<T>} parent
  1302. * The parent prototype to inherit.
  1303. *
  1304. * @param {Highcharts.Dictionary<*>} members
  1305. * A collection of prototype members to add or override compared to the
  1306. * parent prototype.
  1307. *
  1308. * @return {Highcharts.Class<T>}
  1309. * A new prototype.
  1310. */
  1311. function extendClass(parent, members) {
  1312. var obj = (function () { });
  1313. obj.prototype = new parent(); // eslint-disable-line new-cap
  1314. extend(obj.prototype, members);
  1315. return obj;
  1316. }
  1317. /**
  1318. * Left-pad a string to a given length by adding a character repetetively.
  1319. *
  1320. * @function Highcharts.pad
  1321. *
  1322. * @param {number} number
  1323. * The input string or number.
  1324. *
  1325. * @param {number} [length]
  1326. * The desired string length.
  1327. *
  1328. * @param {string} [padder=0]
  1329. * The character to pad with.
  1330. *
  1331. * @return {string}
  1332. * The padded string.
  1333. */
  1334. function pad(number, length, padder) {
  1335. return new Array((length || 2) +
  1336. 1 -
  1337. String(number)
  1338. .replace('-', '')
  1339. .length).join(padder || '0') + number;
  1340. }
  1341. /**
  1342. * Return a length based on either the integer value, or a percentage of a base.
  1343. *
  1344. * @function Highcharts.relativeLength
  1345. *
  1346. * @param {Highcharts.RelativeSize} value
  1347. * A percentage string or a number.
  1348. *
  1349. * @param {number} base
  1350. * The full length that represents 100%.
  1351. *
  1352. * @param {number} [offset=0]
  1353. * A pixel offset to apply for percentage values. Used internally in
  1354. * axis positioning.
  1355. *
  1356. * @return {number}
  1357. * The computed length.
  1358. */
  1359. function relativeLength(value, base, offset) {
  1360. return (/%$/).test(value) ?
  1361. (base * parseFloat(value) / 100) + (offset || 0) :
  1362. parseFloat(value);
  1363. }
  1364. /**
  1365. * Wrap a method with extended functionality, preserving the original function.
  1366. *
  1367. * @function Highcharts.wrap
  1368. *
  1369. * @param {*} obj
  1370. * The context object that the method belongs to. In real cases, this is
  1371. * often a prototype.
  1372. *
  1373. * @param {string} method
  1374. * The name of the method to extend.
  1375. *
  1376. * @param {Highcharts.WrapProceedFunction} func
  1377. * A wrapper function callback. This function is called with the same
  1378. * arguments as the original function, except that the original function
  1379. * is unshifted and passed as the first argument.
  1380. *
  1381. * @return {void}
  1382. */
  1383. function wrap(obj, method, func) {
  1384. var proceed = obj[method];
  1385. obj[method] = function () {
  1386. var args = Array.prototype.slice.call(arguments), outerArgs = arguments, ctx = this, ret;
  1387. ctx.proceed = function () {
  1388. proceed.apply(ctx, arguments.length ? arguments : outerArgs);
  1389. };
  1390. args.unshift(proceed);
  1391. ret = func.apply(this, args);
  1392. ctx.proceed = null;
  1393. return ret;
  1394. };
  1395. }
  1396. /**
  1397. * Recursively converts all Date properties to timestamps.
  1398. *
  1399. * @private
  1400. * @function Highcharts.datePropsToTimestamps
  1401. *
  1402. * @param {*} obj - any object to convert properties of
  1403. *
  1404. * @return {void}
  1405. */
  1406. H.datePropsToTimestamps = function (obj) {
  1407. objectEach(obj, function (val, key) {
  1408. if (isObject(val) && typeof val.getTime === 'function') {
  1409. obj[key] = val.getTime();
  1410. }
  1411. else if (isObject(val) || isArray(val)) {
  1412. H.datePropsToTimestamps(val);
  1413. }
  1414. });
  1415. };
  1416. /**
  1417. * Format a single variable. Similar to sprintf, without the % prefix.
  1418. *
  1419. * @example
  1420. * formatSingle('.2f', 5); // => '5.00'.
  1421. *
  1422. * @function Highcharts.formatSingle
  1423. *
  1424. * @param {string} format
  1425. * The format string.
  1426. *
  1427. * @param {*} val
  1428. * The value.
  1429. *
  1430. * @param {Highcharts.Chart} [chart]
  1431. * A `Chart` instance used to get numberFormatter and time.
  1432. *
  1433. * @return {string}
  1434. * The formatted representation of the value.
  1435. */
  1436. H.formatSingle = function (format, val, chart) {
  1437. var floatRegex = /f$/, decRegex = /\.([0-9])/, lang = H.defaultOptions.lang, decimals;
  1438. var time = chart && chart.time || H.time;
  1439. var numberFormatter = chart && chart.numberFormatter || numberFormat;
  1440. if (floatRegex.test(format)) { // float
  1441. decimals = format.match(decRegex);
  1442. decimals = decimals ? decimals[1] : -1;
  1443. if (val !== null) {
  1444. val = numberFormatter(val, decimals, lang.decimalPoint, format.indexOf(',') > -1 ? lang.thousandsSep : '');
  1445. }
  1446. }
  1447. else {
  1448. val = time.dateFormat(format, val);
  1449. }
  1450. return val;
  1451. };
  1452. /**
  1453. * Format a string according to a subset of the rules of Python's String.format
  1454. * method.
  1455. *
  1456. * @example
  1457. * var s = Highcharts.format(
  1458. * 'The {color} fox was {len:.2f} feet long',
  1459. * { color: 'red', len: Math.PI }
  1460. * );
  1461. * // => The red fox was 3.14 feet long
  1462. *
  1463. * @function Highcharts.format
  1464. *
  1465. * @param {string} str
  1466. * The string to format.
  1467. *
  1468. * @param {*} ctx
  1469. * The context, a collection of key-value pairs where each key is
  1470. * replaced by its value.
  1471. *
  1472. * @param {Highcharts.Chart} [chart]
  1473. * A `Chart` instance used to get numberFormatter and time.
  1474. *
  1475. * @return {string}
  1476. * The formatted string.
  1477. */
  1478. H.format = function (str, ctx, chart) {
  1479. var splitter = '{', isInside = false, segment, valueAndFormat, path, i, len, ret = [], val, index;
  1480. while (str) {
  1481. index = str.indexOf(splitter);
  1482. if (index === -1) {
  1483. break;
  1484. }
  1485. segment = str.slice(0, index);
  1486. if (isInside) { // we're on the closing bracket looking back
  1487. valueAndFormat = segment.split(':');
  1488. // get first and leave
  1489. path = valueAndFormat.shift().split('.');
  1490. len = path.length;
  1491. val = ctx;
  1492. // Assign deeper paths
  1493. for (i = 0; i < len; i++) {
  1494. if (val) {
  1495. val = val[path[i]];
  1496. }
  1497. }
  1498. // Format the replacement
  1499. if (valueAndFormat.length) {
  1500. val = H.formatSingle(valueAndFormat.join(':'), val, chart);
  1501. }
  1502. // Push the result and advance the cursor
  1503. ret.push(val);
  1504. }
  1505. else {
  1506. ret.push(segment);
  1507. }
  1508. str = str.slice(index + 1); // the rest
  1509. isInside = !isInside; // toggle
  1510. splitter = isInside ? '}' : '{'; // now look for next matching bracket
  1511. }
  1512. ret.push(str);
  1513. return ret.join('');
  1514. };
  1515. /**
  1516. * Get the magnitude of a number.
  1517. *
  1518. * @function Highcharts.getMagnitude
  1519. *
  1520. * @param {number} num
  1521. * The number.
  1522. *
  1523. * @return {number}
  1524. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  1525. */
  1526. H.getMagnitude = function (num) {
  1527. return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
  1528. };
  1529. /**
  1530. * Take an interval and normalize it to multiples of round numbers.
  1531. *
  1532. * @deprecated
  1533. * @function Highcharts.normalizeTickInterval
  1534. *
  1535. * @param {number} interval
  1536. * The raw, un-rounded interval.
  1537. *
  1538. * @param {Array<*>} [multiples]
  1539. * Allowed multiples.
  1540. *
  1541. * @param {number} [magnitude]
  1542. * The magnitude of the number.
  1543. *
  1544. * @param {boolean} [allowDecimals]
  1545. * Whether to allow decimals.
  1546. *
  1547. * @param {boolean} [hasTickAmount]
  1548. * If it has tickAmount, avoid landing on tick intervals lower than
  1549. * original.
  1550. *
  1551. * @return {number}
  1552. * The normalized interval.
  1553. *
  1554. * @todo
  1555. * Move this function to the Axis prototype. It is here only for historical
  1556. * reasons.
  1557. */
  1558. H.normalizeTickInterval = function (interval, multiples, magnitude, allowDecimals, hasTickAmount) {
  1559. var normalized, i, retInterval = interval;
  1560. // round to a tenfold of 1, 2, 2.5 or 5
  1561. magnitude = pick(magnitude, 1);
  1562. normalized = interval / magnitude;
  1563. // multiples for a linear scale
  1564. if (!multiples) {
  1565. multiples = hasTickAmount ?
  1566. // Finer grained ticks when the tick amount is hard set, including
  1567. // when alignTicks is true on multiple axes (#4580).
  1568. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  1569. // Else, let ticks fall on rounder numbers
  1570. [1, 2, 2.5, 5, 10];
  1571. // the allowDecimals option
  1572. if (allowDecimals === false) {
  1573. if (magnitude === 1) {
  1574. multiples = multiples.filter(function (num) {
  1575. return num % 1 === 0;
  1576. });
  1577. }
  1578. else if (magnitude <= 0.1) {
  1579. multiples = [1 / magnitude];
  1580. }
  1581. }
  1582. }
  1583. // normalize the interval to the nearest multiple
  1584. for (i = 0; i < multiples.length; i++) {
  1585. retInterval = multiples[i];
  1586. // only allow tick amounts smaller than natural
  1587. if ((hasTickAmount &&
  1588. retInterval * magnitude >= interval) ||
  1589. (!hasTickAmount &&
  1590. (normalized <=
  1591. (multiples[i] +
  1592. (multiples[i + 1] || multiples[i])) / 2))) {
  1593. break;
  1594. }
  1595. }
  1596. // Multiply back to the correct magnitude. Correct floats to appropriate
  1597. // precision (#6085).
  1598. retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
  1599. return retInterval;
  1600. };
  1601. /**
  1602. * Sort an object array and keep the order of equal items. The ECMAScript
  1603. * standard does not specify the behaviour when items are equal.
  1604. *
  1605. * @function Highcharts.stableSort
  1606. *
  1607. * @param {Array<*>} arr
  1608. * The array to sort.
  1609. *
  1610. * @param {Function} sortFunction
  1611. * The function to sort it with, like with regular Array.prototype.sort.
  1612. *
  1613. * @return {void}
  1614. */
  1615. H.stableSort = function (arr, sortFunction) {
  1616. // @todo It seems like Chrome since v70 sorts in a stable way internally,
  1617. // plus all other browsers do it, so over time we may be able to remove this
  1618. // function
  1619. var length = arr.length, sortValue, i;
  1620. // Add index to each item
  1621. for (i = 0; i < length; i++) {
  1622. arr[i].safeI = i; // stable sort index
  1623. }
  1624. arr.sort(function (a, b) {
  1625. sortValue = sortFunction(a, b);
  1626. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1627. });
  1628. // Remove index from items
  1629. for (i = 0; i < length; i++) {
  1630. delete arr[i].safeI; // stable sort index
  1631. }
  1632. };
  1633. /**
  1634. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1635. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1636. * than 150.000 points. This method is slightly slower, but safe.
  1637. *
  1638. * @function Highcharts.arrayMin
  1639. *
  1640. * @param {Array<*>} data
  1641. * An array of numbers.
  1642. *
  1643. * @return {number}
  1644. * The lowest number.
  1645. */
  1646. function arrayMin(data) {
  1647. var i = data.length, min = data[0];
  1648. while (i--) {
  1649. if (data[i] < min) {
  1650. min = data[i];
  1651. }
  1652. }
  1653. return min;
  1654. }
  1655. /**
  1656. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1657. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1658. * than 150.000 points. This method is slightly slower, but safe.
  1659. *
  1660. * @function Highcharts.arrayMax
  1661. *
  1662. * @param {Array<*>} data
  1663. * An array of numbers.
  1664. *
  1665. * @return {number}
  1666. * The highest number.
  1667. */
  1668. function arrayMax(data) {
  1669. var i = data.length, max = data[0];
  1670. while (i--) {
  1671. if (data[i] > max) {
  1672. max = data[i];
  1673. }
  1674. }
  1675. return max;
  1676. }
  1677. /**
  1678. * Utility method that destroys any SVGElement instances that are properties on
  1679. * the given object. It loops all properties and invokes destroy if there is a
  1680. * destroy method. The property is then delete.
  1681. *
  1682. * @function Highcharts.destroyObjectProperties
  1683. *
  1684. * @param {*} obj
  1685. * The object to destroy properties on.
  1686. *
  1687. * @param {*} [except]
  1688. * Exception, do not destroy this property, only delete it.
  1689. *
  1690. * @return {void}
  1691. */
  1692. function destroyObjectProperties(obj, except) {
  1693. objectEach(obj, function (val, n) {
  1694. // If the object is non-null and destroy is defined
  1695. if (val && val !== except && val.destroy) {
  1696. // Invoke the destroy
  1697. val.destroy();
  1698. }
  1699. // Delete the property from the object.
  1700. delete obj[n];
  1701. });
  1702. }
  1703. /**
  1704. * Discard a HTML element by moving it to the bin and delete.
  1705. *
  1706. * @function Highcharts.discardElement
  1707. *
  1708. * @param {Highcharts.HTMLDOMElement} element
  1709. * The HTML node to discard.
  1710. *
  1711. * @return {void}
  1712. */
  1713. function discardElement(element) {
  1714. var garbageBin = H.garbageBin;
  1715. // create a garbage bin element, not part of the DOM
  1716. if (!garbageBin) {
  1717. garbageBin = H.createElement('div');
  1718. }
  1719. // move the node and empty bin
  1720. if (element) {
  1721. garbageBin.appendChild(element);
  1722. }
  1723. garbageBin.innerHTML = '';
  1724. }
  1725. /**
  1726. * Fix JS round off float errors.
  1727. *
  1728. * @function Highcharts.correctFloat
  1729. *
  1730. * @param {number} num
  1731. * A float number to fix.
  1732. *
  1733. * @param {number} [prec=14]
  1734. * The precision.
  1735. *
  1736. * @return {number}
  1737. * The corrected float number.
  1738. */
  1739. function correctFloat(num, prec) {
  1740. return parseFloat(num.toPrecision(prec || 14));
  1741. }
  1742. /**
  1743. * Set the global animation to either a given value, or fall back to the given
  1744. * chart's animation option.
  1745. *
  1746. * @function Highcharts.setAnimation
  1747. *
  1748. * @param {boolean|Highcharts.AnimationOptionsObject|undefined} animation
  1749. * The animation object.
  1750. *
  1751. * @param {Highcharts.Chart} chart
  1752. * The chart instance.
  1753. *
  1754. * @return {void}
  1755. *
  1756. * @todo
  1757. * This function always relates to a chart, and sets a property on the renderer,
  1758. * so it should be moved to the SVGRenderer.
  1759. */
  1760. function setAnimation(animation, chart) {
  1761. chart.renderer.globalAnimation = pick(animation, chart.options.chart.animation, true);
  1762. }
  1763. /**
  1764. * Get the animation in object form, where a disabled animation is always
  1765. * returned as `{ duration: 0 }`.
  1766. *
  1767. * @function Highcharts.animObject
  1768. *
  1769. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
  1770. * An animation setting. Can be an object with duration, complete and
  1771. * easing properties, or a boolean to enable or disable.
  1772. *
  1773. * @return {Highcharts.AnimationOptionsObject}
  1774. * An object with at least a duration property.
  1775. */
  1776. function animObject(animation) {
  1777. return isObject(animation) ?
  1778. H.merge(animation) :
  1779. { duration: animation ? 500 : 0 };
  1780. }
  1781. /**
  1782. * The time unit lookup
  1783. *
  1784. * @ignore
  1785. */
  1786. H.timeUnits = {
  1787. millisecond: 1,
  1788. second: 1000,
  1789. minute: 60000,
  1790. hour: 3600000,
  1791. day: 24 * 3600000,
  1792. week: 7 * 24 * 3600000,
  1793. month: 28 * 24 * 3600000,
  1794. year: 364 * 24 * 3600000
  1795. };
  1796. /**
  1797. * Format a number and return a string based on input settings.
  1798. *
  1799. * @sample highcharts/members/highcharts-numberformat/
  1800. * Custom number format
  1801. *
  1802. * @function Highcharts.numberFormat
  1803. *
  1804. * @param {number} number
  1805. * The input number to format.
  1806. *
  1807. * @param {number} decimals
  1808. * The amount of decimals. A value of -1 preserves the amount in the
  1809. * input number.
  1810. *
  1811. * @param {string} [decimalPoint]
  1812. * The decimal point, defaults to the one given in the lang options, or
  1813. * a dot.
  1814. *
  1815. * @param {string} [thousandsSep]
  1816. * The thousands separator, defaults to the one given in the lang
  1817. * options, or a space character.
  1818. *
  1819. * @return {string}
  1820. * The formatted number.
  1821. */
  1822. function numberFormat(number, decimals, decimalPoint, thousandsSep) {
  1823. number = +number || 0;
  1824. decimals = +decimals;
  1825. var lang = H.defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, strinteger, thousands, ret, roundedNumber, exponent = number.toString().split('e'), fractionDigits;
  1826. if (decimals === -1) {
  1827. // Preserve decimals. Not huge numbers (#3793).
  1828. decimals = Math.min(origDec, 20);
  1829. }
  1830. else if (!isNumber(decimals)) {
  1831. decimals = 2;
  1832. }
  1833. else if (decimals && exponent[1] && exponent[1] < 0) {
  1834. // Expose decimals from exponential notation (#7042)
  1835. fractionDigits = decimals + +exponent[1];
  1836. if (fractionDigits >= 0) {
  1837. // remove too small part of the number while keeping the notation
  1838. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  1839. .split('e')[0];
  1840. decimals = fractionDigits;
  1841. }
  1842. else {
  1843. // fractionDigits < 0
  1844. exponent[0] = exponent[0].split('.')[0] || 0;
  1845. if (decimals < 20) {
  1846. // use number instead of exponential notation (#7405)
  1847. number = (exponent[0] * Math.pow(10, exponent[1]))
  1848. .toFixed(decimals);
  1849. }
  1850. else {
  1851. // or zero
  1852. number = 0;
  1853. }
  1854. exponent[1] = 0;
  1855. }
  1856. }
  1857. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  1858. // Then use toFixed to handle rounding.
  1859. roundedNumber = (Math.abs(exponent[1] ? exponent[0] : number) +
  1860. Math.pow(10, -Math.max(decimals, origDec) - 1)).toFixed(decimals);
  1861. // A string containing the positive integer component of the number
  1862. strinteger = String(pInt(roundedNumber));
  1863. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  1864. thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  1865. // Language
  1866. decimalPoint = pick(decimalPoint, lang.decimalPoint);
  1867. thousandsSep = pick(thousandsSep, lang.thousandsSep);
  1868. // Start building the return
  1869. ret = number < 0 ? '-' : '';
  1870. // Add the leftover after grouping into thousands. For example, in the
  1871. // number 42 000 000, this line adds 42.
  1872. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  1873. // Add the remaining thousands groups, joined by the thousands separator
  1874. ret += strinteger
  1875. .substr(thousands)
  1876. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  1877. // Add the decimal point and the decimal component
  1878. if (decimals) {
  1879. // Get the decimal component
  1880. ret += decimalPoint + roundedNumber.slice(-decimals);
  1881. }
  1882. if (exponent[1] && +ret !== 0) {
  1883. ret += 'e' + exponent[1];
  1884. }
  1885. return ret;
  1886. }
  1887. /**
  1888. * Easing definition
  1889. *
  1890. * @private
  1891. * @function Math.easeInOutSine
  1892. *
  1893. * @param {number} pos
  1894. * Current position, ranging from 0 to 1.
  1895. *
  1896. * @return {number}
  1897. * Ease result
  1898. */
  1899. Math.easeInOutSine = function (pos) {
  1900. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1901. };
  1902. /**
  1903. * Get the computed CSS value for given element and property, only for numerical
  1904. * properties. For width and height, the dimension of the inner box (excluding
  1905. * padding) is returned. Used for fitting the chart within the container.
  1906. *
  1907. * @function Highcharts.getStyle
  1908. *
  1909. * @param {Highcharts.HTMLDOMElement} el
  1910. * An HTML element.
  1911. *
  1912. * @param {string} prop
  1913. * The property name.
  1914. *
  1915. * @param {boolean} [toInt=true]
  1916. * Parse to integer.
  1917. *
  1918. * @return {number|string}
  1919. * The numeric value.
  1920. */
  1921. H.getStyle = function (el, prop, toInt) {
  1922. var style;
  1923. // For width and height, return the actual inner pixel size (#4913)
  1924. if (prop === 'width') {
  1925. var offsetWidth = Math.min(el.offsetWidth, el.scrollWidth);
  1926. // In flex boxes, we need to use getBoundingClientRect and floor it,
  1927. // because scrollWidth doesn't support subpixel precision (#6427) ...
  1928. var boundingClientRectWidth = el.getBoundingClientRect &&
  1929. el.getBoundingClientRect().width;
  1930. // ...unless if the containing div or its parents are transform-scaled
  1931. // down, in which case the boundingClientRect can't be used as it is
  1932. // also scaled down (#9871, #10498).
  1933. if (boundingClientRectWidth < offsetWidth &&
  1934. boundingClientRectWidth >= offsetWidth - 1) {
  1935. offsetWidth = Math.floor(boundingClientRectWidth);
  1936. }
  1937. return Math.max(0, // #8377
  1938. (offsetWidth -
  1939. H.getStyle(el, 'padding-left') -
  1940. H.getStyle(el, 'padding-right')));
  1941. }
  1942. if (prop === 'height') {
  1943. return Math.max(0, // #8377
  1944. Math.min(el.offsetHeight, el.scrollHeight) -
  1945. H.getStyle(el, 'padding-top') -
  1946. H.getStyle(el, 'padding-bottom'));
  1947. }
  1948. if (!win.getComputedStyle) {
  1949. // SVG not supported, forgot to load oldie.js?
  1950. H.error(27, true);
  1951. }
  1952. // Otherwise, get the computed style
  1953. style = win.getComputedStyle(el, undefined); // eslint-disable-line no-undefined
  1954. if (style) {
  1955. style = style.getPropertyValue(prop);
  1956. if (pick(toInt, prop !== 'opacity')) {
  1957. style = pInt(style);
  1958. }
  1959. }
  1960. return style;
  1961. };
  1962. /**
  1963. * Search for an item in an array.
  1964. *
  1965. * @function Highcharts.inArray
  1966. *
  1967. * @deprecated
  1968. *
  1969. * @param {*} item
  1970. * The item to search for.
  1971. *
  1972. * @param {Array<*>} arr
  1973. * The array or node collection to search in.
  1974. *
  1975. * @param {number} [fromIndex=0]
  1976. * The index to start searching from.
  1977. *
  1978. * @return {number}
  1979. * The index within the array, or -1 if not found.
  1980. */
  1981. H.inArray = function (item, arr, fromIndex) {
  1982. return arr.indexOf(item, fromIndex);
  1983. };
  1984. /* eslint-disable valid-jsdoc */
  1985. /**
  1986. * Return the value of the first element in the array that satisfies the
  1987. * provided testing function.
  1988. *
  1989. * @function Highcharts.find<T>
  1990. *
  1991. * @param {Array<T>} arr
  1992. * The array to test.
  1993. *
  1994. * @param {Function} callback
  1995. * The callback function. The function receives the item as the first
  1996. * argument. Return `true` if this item satisfies the condition.
  1997. *
  1998. * @return {T|undefined}
  1999. * The value of the element.
  2000. */
  2001. H.find = Array.prototype.find ?
  2002. /* eslint-enable valid-jsdoc */
  2003. function (arr, callback) {
  2004. return arr.find(callback);
  2005. } :
  2006. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  2007. function (arr, callback) {
  2008. var i, length = arr.length;
  2009. for (i = 0; i < length; i++) {
  2010. if (callback(arr[i], i)) { // eslint-disable-line callback-return
  2011. return arr[i];
  2012. }
  2013. }
  2014. };
  2015. /**
  2016. * Returns an array of a given object's own properties.
  2017. *
  2018. * @function Highcharts.keys
  2019. * @deprecated
  2020. *
  2021. * @param {*} obj
  2022. * The object of which the properties are to be returned.
  2023. *
  2024. * @return {Array<string>}
  2025. * An array of strings that represents all the properties.
  2026. */
  2027. H.keys = Object.keys;
  2028. /**
  2029. * Get the element's offset position, corrected for `overflow: auto`.
  2030. *
  2031. * @function Highcharts.offset
  2032. *
  2033. * @param {Highcharts.HTMLDOMElement} el
  2034. * The HTML element.
  2035. *
  2036. * @return {Highcharts.OffsetObject}
  2037. * An object containing `left` and `top` properties for the position in
  2038. * the page.
  2039. */
  2040. function offset(el) {
  2041. var docElem = doc.documentElement, box = (el.parentElement || el.parentNode) ?
  2042. el.getBoundingClientRect() :
  2043. { top: 0, left: 0 };
  2044. return {
  2045. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  2046. (docElem.clientTop || 0),
  2047. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  2048. (docElem.clientLeft || 0)
  2049. };
  2050. }
  2051. /**
  2052. * Stop running animation.
  2053. *
  2054. * @function Highcharts.stop
  2055. *
  2056. * @param {Highcharts.SVGElement} el
  2057. * The SVGElement to stop animation on.
  2058. *
  2059. * @param {string} [prop]
  2060. * The property to stop animating. If given, the stop method will stop a
  2061. * single property from animating, while others continue.
  2062. *
  2063. * @return {void}
  2064. *
  2065. * @todo
  2066. * A possible extension to this would be to stop a single property, when
  2067. * we want to continue animating others. Then assign the prop to the timer
  2068. * in the Fx.run method, and check for the prop here. This would be an
  2069. * improvement in all cases where we stop the animation from .attr. Instead of
  2070. * stopping everything, we can just stop the actual attributes we're setting.
  2071. */
  2072. H.stop = function (el, prop) {
  2073. var i = H.timers.length;
  2074. // Remove timers related to this element (#4519)
  2075. while (i--) {
  2076. if (H.timers[i].elem === el && (!prop || prop === H.timers[i].prop)) {
  2077. H.timers[i].stopped = true; // #4667
  2078. }
  2079. }
  2080. };
  2081. /* eslint-disable valid-jsdoc */
  2082. /**
  2083. * Iterate over object key pairs in an object.
  2084. *
  2085. * @function Highcharts.objectEach<T>
  2086. *
  2087. * @param {*} obj
  2088. * The object to iterate over.
  2089. *
  2090. * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
  2091. * The iterator callback. It passes three arguments:
  2092. * * value - The property value.
  2093. * * key - The property key.
  2094. * * obj - The object that objectEach is being applied to.
  2095. *
  2096. * @param {T} [ctx]
  2097. * The context.
  2098. *
  2099. * @return {void}
  2100. */
  2101. function objectEach(obj, fn, ctx) {
  2102. /* eslint-enable valid-jsdoc */
  2103. for (var key in obj) {
  2104. if (Object.hasOwnProperty.call(obj, key)) {
  2105. fn.call(ctx || obj[key], obj[key], key, obj);
  2106. }
  2107. }
  2108. }
  2109. /**
  2110. * Iterate over an array.
  2111. *
  2112. * @deprecated
  2113. * @function Highcharts.each
  2114. *
  2115. * @param {Array<*>} arr
  2116. * The array to iterate over.
  2117. *
  2118. * @param {Function} fn
  2119. * The iterator callback. It passes three arguments:
  2120. * - `item`: The array item.
  2121. * - `index`: The item's index in the array.
  2122. * - `arr`: The array that each is being applied to.
  2123. *
  2124. * @param {*} [ctx]
  2125. * The context.
  2126. *
  2127. * @return {void}
  2128. */
  2129. /**
  2130. * Filter an array by a callback.
  2131. *
  2132. * @deprecated
  2133. * @function Highcharts.grep
  2134. *
  2135. * @param {Array<*>} arr
  2136. * The array to filter.
  2137. *
  2138. * @param {Function} callback
  2139. * The callback function. The function receives the item as the first
  2140. * argument. Return `true` if the item is to be preserved.
  2141. *
  2142. * @return {Array<*>}
  2143. * A new, filtered array.
  2144. */
  2145. /**
  2146. * Map an array by a callback.
  2147. *
  2148. * @deprecated
  2149. * @function Highcharts.map
  2150. *
  2151. * @param {Array<*>} arr
  2152. * The array to map.
  2153. *
  2154. * @param {Function} fn
  2155. * The callback function. Return the new value for the new array.
  2156. *
  2157. * @return {Array<*>}
  2158. * A new array item with modified items.
  2159. */
  2160. /**
  2161. * Reduce an array to a single value.
  2162. *
  2163. * @deprecated
  2164. * @function Highcharts.reduce
  2165. *
  2166. * @param {Array<*>} arr
  2167. * The array to reduce.
  2168. *
  2169. * @param {Function} fn
  2170. * The callback function. Return the reduced value. Receives 4
  2171. * arguments: Accumulated/reduced value, current value, current array
  2172. * index, and the array.
  2173. *
  2174. * @param {*} initialValue
  2175. * The initial value of the accumulator.
  2176. *
  2177. * @return {*}
  2178. * The reduced value.
  2179. */
  2180. /**
  2181. * Test whether at least one element in the array passes the test implemented by
  2182. * the provided function.
  2183. *
  2184. * @deprecated
  2185. * @function Highcharts.some
  2186. *
  2187. * @param {Array<*>} arr
  2188. * The array to test
  2189. *
  2190. * @param {Function} fn
  2191. * The function to run on each item. Return truty to pass the test.
  2192. * Receives arguments `currentValue`, `index` and `array`.
  2193. *
  2194. * @param {*} ctx
  2195. * The context.
  2196. *
  2197. * @return {boolean}
  2198. */
  2199. objectEach({
  2200. map: 'map',
  2201. each: 'forEach',
  2202. grep: 'filter',
  2203. reduce: 'reduce',
  2204. some: 'some'
  2205. }, function (val, key) {
  2206. H[key] = function (arr) {
  2207. return Array.prototype[val].apply(arr, [].slice.call(arguments, 1));
  2208. };
  2209. });
  2210. /* eslint-disable valid-jsdoc */
  2211. /**
  2212. * Add an event listener.
  2213. *
  2214. * @function Highcharts.addEvent<T>
  2215. *
  2216. * @param {Highcharts.Class<T>|T} el
  2217. * The element or object to add a listener to. It can be a
  2218. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  2219. *
  2220. * @param {string} type
  2221. * The event type.
  2222. *
  2223. * @param {Highcharts.EventCallbackFunction<T>|Function} fn
  2224. * The function callback to execute when the event is fired.
  2225. *
  2226. * @param {Highcharts.EventOptionsObject} [options]
  2227. * Options for adding the event.
  2228. *
  2229. * @return {Function}
  2230. * A callback function to remove the added event.
  2231. */
  2232. H.addEvent = function (el, type, fn, options) {
  2233. if (options === void 0) { options = {}; }
  2234. /* eslint-enable valid-jsdoc */
  2235. var events, addEventListener = (el.addEventListener || H.addEventListenerPolyfill);
  2236. // If we're setting events directly on the constructor, use a separate
  2237. // collection, `protoEvents` to distinguish it from the item events in
  2238. // `hcEvents`.
  2239. if (typeof el === 'function' && el.prototype) {
  2240. events = el.prototype.protoEvents = el.prototype.protoEvents || {};
  2241. }
  2242. else {
  2243. events = el.hcEvents = el.hcEvents || {};
  2244. }
  2245. // Allow click events added to points, otherwise they will be prevented by
  2246. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  2247. if (H.Point &&
  2248. el instanceof H.Point &&
  2249. el.series &&
  2250. el.series.chart) {
  2251. el.series.chart.runTrackerClick = true;
  2252. }
  2253. // Handle DOM events
  2254. if (addEventListener) {
  2255. addEventListener.call(el, type, fn, false);
  2256. }
  2257. if (!events[type]) {
  2258. events[type] = [];
  2259. }
  2260. var eventObject = {
  2261. fn: fn,
  2262. order: typeof options.order === 'number' ? options.order : Infinity
  2263. };
  2264. events[type].push(eventObject);
  2265. // Order the calls
  2266. events[type].sort(function (a, b) {
  2267. return a.order - b.order;
  2268. });
  2269. // Return a function that can be called to remove this event.
  2270. return function () {
  2271. H.removeEvent(el, type, fn);
  2272. };
  2273. };
  2274. /* eslint-disable valid-jsdoc */
  2275. /**
  2276. * Remove an event that was added with {@link Highcharts#addEvent}.
  2277. *
  2278. * @function Highcharts.removeEvent<T>
  2279. *
  2280. * @param {Highcharts.Class<T>|T} el
  2281. * The element to remove events on.
  2282. *
  2283. * @param {string} [type]
  2284. * The type of events to remove. If undefined, all events are removed
  2285. * from the element.
  2286. *
  2287. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  2288. * The specific callback to remove. If undefined, all events that match
  2289. * the element and optionally the type are removed.
  2290. *
  2291. * @return {void}
  2292. */
  2293. H.removeEvent = function (el, type, fn) {
  2294. /* eslint-enable valid-jsdoc */
  2295. var events;
  2296. /**
  2297. * @private
  2298. * @param {string} type - event type
  2299. * @param {Highcharts.EventCallbackFunction<T>} fn - callback
  2300. * @return {void}
  2301. */
  2302. function removeOneEvent(type, fn) {
  2303. var removeEventListener = (el.removeEventListener || H.removeEventListenerPolyfill);
  2304. if (removeEventListener) {
  2305. removeEventListener.call(el, type, fn, false);
  2306. }
  2307. }
  2308. /**
  2309. * @private
  2310. * @param {any} eventCollection - collection
  2311. * @return {void}
  2312. */
  2313. function removeAllEvents(eventCollection) {
  2314. var types, len;
  2315. if (!el.nodeName) {
  2316. return; // break on non-DOM events
  2317. }
  2318. if (type) {
  2319. types = {};
  2320. types[type] = true;
  2321. }
  2322. else {
  2323. types = eventCollection;
  2324. }
  2325. objectEach(types, function (val, n) {
  2326. if (eventCollection[n]) {
  2327. len = eventCollection[n].length;
  2328. while (len--) {
  2329. removeOneEvent(n, eventCollection[n][len].fn);
  2330. }
  2331. }
  2332. });
  2333. }
  2334. ['protoEvents', 'hcEvents'].forEach(function (coll, i) {
  2335. var eventElem = i ? el : el.prototype;
  2336. var eventCollection = eventElem && eventElem[coll];
  2337. if (eventCollection) {
  2338. if (type) {
  2339. events = (eventCollection[type] || []);
  2340. if (fn) {
  2341. eventCollection[type] = events.filter(function (obj) {
  2342. return fn !== obj.fn;
  2343. });
  2344. removeOneEvent(type, fn);
  2345. }
  2346. else {
  2347. removeAllEvents(eventCollection);
  2348. eventCollection[type] = [];
  2349. }
  2350. }
  2351. else {
  2352. removeAllEvents(eventCollection);
  2353. eventElem[coll] = {};
  2354. }
  2355. }
  2356. });
  2357. };
  2358. /* eslint-disable valid-jsdoc */
  2359. /**
  2360. * Fire an event that was registered with {@link Highcharts#addEvent}.
  2361. *
  2362. * @function Highcharts.fireEvent<T>
  2363. *
  2364. * @param {T} el
  2365. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  2366. * an {@link SVGElement} or any other object.
  2367. *
  2368. * @param {string} type
  2369. * The type of event.
  2370. *
  2371. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  2372. * Custom event arguments that are passed on as an argument to the event
  2373. * handler.
  2374. *
  2375. * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
  2376. * The default function to execute if the other listeners haven't
  2377. * returned false.
  2378. *
  2379. * @return {void}
  2380. */
  2381. H.fireEvent = function (el, type, eventArguments, defaultFunction) {
  2382. /* eslint-enable valid-jsdoc */
  2383. var e, i;
  2384. eventArguments = eventArguments || {};
  2385. if (doc.createEvent &&
  2386. (el.dispatchEvent || el.fireEvent)) {
  2387. e = doc.createEvent('Events');
  2388. e.initEvent(type, true, true);
  2389. extend(e, eventArguments);
  2390. if (el.dispatchEvent) {
  2391. el.dispatchEvent(e);
  2392. }
  2393. else {
  2394. el.fireEvent(type, e);
  2395. }
  2396. }
  2397. else {
  2398. if (!eventArguments.target) {
  2399. // We're running a custom event
  2400. extend(eventArguments, {
  2401. // Attach a simple preventDefault function to skip
  2402. // default handler if called. The built-in
  2403. // defaultPrevented property is not overwritable (#5112)
  2404. preventDefault: function () {
  2405. eventArguments.defaultPrevented = true;
  2406. },
  2407. // Setting target to native events fails with clicking
  2408. // the zoom-out button in Chrome.
  2409. target: el,
  2410. // If the type is not set, we're running a custom event
  2411. // (#2297). If it is set, we're running a browser event,
  2412. // and setting it will cause en error in IE8 (#2465).
  2413. type: type
  2414. });
  2415. }
  2416. var fireInOrder = function (protoEvents, hcEvents) {
  2417. if (protoEvents === void 0) { protoEvents = []; }
  2418. if (hcEvents === void 0) { hcEvents = []; }
  2419. var iA = 0;
  2420. var iB = 0;
  2421. var length = protoEvents.length + hcEvents.length;
  2422. for (i = 0; i < length; i++) {
  2423. var obj = (!protoEvents[iA] ?
  2424. hcEvents[iB++] :
  2425. !hcEvents[iB] ?
  2426. protoEvents[iA++] :
  2427. protoEvents[iA].order <= hcEvents[iB].order ?
  2428. protoEvents[iA++] :
  2429. hcEvents[iB++]);
  2430. // If the event handler return false, prevent the default
  2431. // handler from executing
  2432. if (obj.fn.call(el, eventArguments) === false) {
  2433. eventArguments.preventDefault();
  2434. }
  2435. }
  2436. };
  2437. fireInOrder(el.protoEvents && el.protoEvents[type], el.hcEvents && el.hcEvents[type]);
  2438. }
  2439. // Run the default if not prevented
  2440. if (defaultFunction && !eventArguments.defaultPrevented) {
  2441. defaultFunction.call(el, eventArguments);
  2442. }
  2443. };
  2444. /**
  2445. * The global animate method, which uses Fx to create individual animators.
  2446. *
  2447. * @function Highcharts.animate
  2448. *
  2449. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  2450. * The element to animate.
  2451. *
  2452. * @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
  2453. * An object containing key-value pairs of the properties to animate.
  2454. * Supports numeric as pixel-based CSS properties for HTML objects and
  2455. * attributes for SVGElements.
  2456. *
  2457. * @param {Highcharts.AnimationOptionsObject} [opt]
  2458. * Animation options.
  2459. *
  2460. * @return {void}
  2461. */
  2462. H.animate = function (el, params, opt) {
  2463. var start, unit = '', end, fx, args;
  2464. if (!isObject(opt)) { // Number or undefined/null
  2465. args = arguments;
  2466. opt = {
  2467. duration: args[2],
  2468. easing: args[3],
  2469. complete: args[4]
  2470. };
  2471. }
  2472. if (!isNumber(opt.duration)) {
  2473. opt.duration = 400;
  2474. }
  2475. opt.easing = typeof opt.easing === 'function' ?
  2476. opt.easing :
  2477. (Math[opt.easing] || Math.easeInOutSine);
  2478. opt.curAnim = H.merge(params);
  2479. objectEach(params, function (val, prop) {
  2480. // Stop current running animation of this property
  2481. H.stop(el, prop);
  2482. fx = new H.Fx(el, opt, prop);
  2483. end = null;
  2484. if (prop === 'd') {
  2485. fx.paths = fx.initPath(el, el.d, params.d);
  2486. fx.toD = params.d;
  2487. start = 0;
  2488. end = 1;
  2489. }
  2490. else if (el.attr) {
  2491. start = el.attr(prop);
  2492. }
  2493. else {
  2494. start = parseFloat(H.getStyle(el, prop)) || 0;
  2495. if (prop !== 'opacity') {
  2496. unit = 'px';
  2497. }
  2498. }
  2499. if (!end) {
  2500. end = val;
  2501. }
  2502. if (end && end.match && end.match('px')) {
  2503. end = end.replace(/px/g, ''); // #4351
  2504. }
  2505. fx.run(start, end, unit);
  2506. });
  2507. };
  2508. /**
  2509. * Factory to create new series prototypes.
  2510. *
  2511. * @function Highcharts.seriesType
  2512. *
  2513. * @param {string} type
  2514. * The series type name.
  2515. *
  2516. * @param {string} parent
  2517. * The parent series type name. Use `line` to inherit from the basic
  2518. * {@link Series} object.
  2519. *
  2520. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  2521. * The additional default options that are merged with the parent's
  2522. * options.
  2523. *
  2524. * @param {Highcharts.Dictionary<*>} [props]
  2525. * The properties (functions and primitives) to set on the new
  2526. * prototype.
  2527. *
  2528. * @param {Highcharts.Dictionary<*>} [pointProps]
  2529. * Members for a series-specific extension of the {@link Point}
  2530. * prototype if needed.
  2531. *
  2532. * @return {Highcharts.Series}
  2533. * The newly created prototype as extended from {@link Series} or its
  2534. * derivatives.
  2535. */
  2536. // docs: add to API + extending Highcharts
  2537. H.seriesType = function (type, parent, options, props, pointProps) {
  2538. var defaultOptions = H.getOptions(), seriesTypes = H.seriesTypes;
  2539. // Merge the options
  2540. defaultOptions.plotOptions[type] = H.merge(defaultOptions.plotOptions[parent], options);
  2541. // Create the class
  2542. seriesTypes[type] = extendClass(seriesTypes[parent] || function () { }, props);
  2543. seriesTypes[type].prototype.type = type;
  2544. // Create the point class if needed
  2545. if (pointProps) {
  2546. seriesTypes[type].prototype.pointClass =
  2547. extendClass(H.Point, pointProps);
  2548. }
  2549. return seriesTypes[type];
  2550. };
  2551. /**
  2552. * Get a unique key for using in internal element id's and pointers. The key is
  2553. * composed of a random hash specific to this Highcharts instance, and a
  2554. * counter.
  2555. *
  2556. * @example
  2557. * var id = H.uniqueKey(); // => 'highcharts-x45f6hp-0'
  2558. *
  2559. * @function Highcharts.uniqueKey
  2560. *
  2561. * @return {string}
  2562. * A unique key.
  2563. */
  2564. H.uniqueKey = (function () {
  2565. var uniqueKeyHash = Math.random().toString(36).substring(2, 9), idCounter = 0;
  2566. return function () {
  2567. return 'highcharts-' + uniqueKeyHash + '-' + idCounter++;
  2568. };
  2569. }());
  2570. H.isFunction = function (obj) {
  2571. return typeof obj === 'function';
  2572. };
  2573. // Register Highcharts as a plugin in jQuery
  2574. if (win.jQuery) {
  2575. /**
  2576. * Highcharts-extended JQuery.
  2577. *
  2578. * @external JQuery
  2579. */
  2580. /**
  2581. * Helper function to return the chart of the current JQuery selector
  2582. * element.
  2583. *
  2584. * @function external:JQuery#highcharts
  2585. *
  2586. * @return {Highcharts.Chart}
  2587. * The chart that is linked to the JQuery selector element.
  2588. */ /**
  2589. * Factory function to create a chart in the current JQuery selector
  2590. * element.
  2591. *
  2592. * @function external:JQuery#highcharts
  2593. *
  2594. * @param {'Chart'|'Map'|'StockChart'|string} [className]
  2595. * Name of the factory class in the Highcharts namespace.
  2596. *
  2597. * @param {Highcharts.Options} [options]
  2598. * The chart options structure.
  2599. *
  2600. * @param {Highcharts.ChartCallbackFunction} [callback]
  2601. * Function to run when the chart has loaded and and all external
  2602. * images are loaded. Defining a
  2603. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  2604. * handler is equivalent.
  2605. *
  2606. * @return {JQuery}
  2607. * The current JQuery selector.
  2608. */
  2609. win.jQuery.fn.highcharts = function () {
  2610. var args = [].slice.call(arguments);
  2611. if (this[0]) { // this[0] is the renderTo div
  2612. // Create the chart
  2613. if (args[0]) {
  2614. new H[ // eslint-disable-line computed-property-spacing, no-new
  2615. // Constructor defaults to Chart
  2616. isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
  2617. return this;
  2618. }
  2619. // When called without parameters or with the return argument,
  2620. // return an existing chart
  2621. return charts[attr(this[0], 'data-highcharts-chart')];
  2622. }
  2623. };
  2624. }
  2625. // TODO use named exports when supported.
  2626. var utils = {
  2627. animObject: animObject,
  2628. arrayMax: arrayMax,
  2629. arrayMin: arrayMin,
  2630. attr: attr,
  2631. clamp: clamp,
  2632. correctFloat: correctFloat,
  2633. defined: defined,
  2634. destroyObjectProperties: destroyObjectProperties,
  2635. discardElement: discardElement,
  2636. erase: erase,
  2637. extend: extend,
  2638. extendClass: extendClass,
  2639. isArray: isArray,
  2640. isClass: isClass,
  2641. isDOMElement: isDOMElement,
  2642. isNumber: isNumber,
  2643. isObject: isObject,
  2644. isString: isString,
  2645. numberFormat: numberFormat,
  2646. objectEach: objectEach,
  2647. offset: offset,
  2648. pad: pad,
  2649. pick: pick,
  2650. pInt: pInt,
  2651. relativeLength: relativeLength,
  2652. setAnimation: setAnimation,
  2653. splat: splat,
  2654. syncTimeout: syncTimeout,
  2655. wrap: wrap
  2656. };
  2657. return utils;
  2658. });
  2659. _registerModule(_modules, 'parts/Color.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  2660. /* *
  2661. *
  2662. * (c) 2010-2019 Torstein Honsi
  2663. *
  2664. * License: www.highcharts.com/license
  2665. *
  2666. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2667. *
  2668. * */
  2669. /**
  2670. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  2671. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  2672. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  2673. * browsers and displayed correctly, but Highcharts is not able to process them
  2674. * and apply concepts like opacity and brightening.
  2675. *
  2676. * @typedef {string} Highcharts.ColorString
  2677. */
  2678. /**
  2679. * A valid color type than can be parsed and handled by Highcharts. It can be a
  2680. * color string, a gradient object, or a pattern object.
  2681. *
  2682. * @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
  2683. */
  2684. /**
  2685. * Gradient options instead of a solid color.
  2686. *
  2687. * @example
  2688. * // Linear gradient used as a color option
  2689. * color: {
  2690. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  2691. * stops: [
  2692. * [0, '#003399'], // start
  2693. * [0.5, '#ffffff'], // middle
  2694. * [1, '#3366AA'] // end
  2695. * ]
  2696. * }
  2697. *
  2698. * @interface Highcharts.GradientColorObject
  2699. */ /**
  2700. * Holds an object that defines the start position and the end position relative
  2701. * to the shape.
  2702. * @name Highcharts.GradientColorObject#linearGradient
  2703. * @type {Highcharts.LinearGradientColorObject|undefined}
  2704. */ /**
  2705. * Holds an object that defines the center position and the radius.
  2706. * @name Highcharts.GradientColorObject#radialGradient
  2707. * @type {Highcharts.RadialGradientColorObject|undefined}
  2708. */ /**
  2709. * The first item in each tuple is the position in the gradient, where 0 is the
  2710. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  2711. * applied. The second item is the color for each stop. This color can also be
  2712. * given in the rgba format.
  2713. * @name Highcharts.GradientColorObject#stops
  2714. * @type {Array<Highcharts.GradientColorStopObject>}
  2715. */
  2716. /**
  2717. * Color stop tuple.
  2718. *
  2719. * @see Highcharts.GradientColorObject
  2720. *
  2721. * @interface Highcharts.GradientColorStopObject
  2722. */ /**
  2723. * @name Highcharts.GradientColorStopObject#0
  2724. * @type {number}
  2725. */ /**
  2726. * @name Highcharts.GradientColorStopObject#1
  2727. * @type {Highcharts.ColorString}
  2728. */ /**
  2729. * @name Highcharts.GradoentColorStopObject#color
  2730. * @type {Highcharts.Color|undefined}
  2731. */
  2732. /**
  2733. * Defines the start position and the end position for a gradient relative
  2734. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  2735. * to the shape, where 0 means top/left and 1 is bottom/right.
  2736. *
  2737. * @interface Highcharts.LinearGradientColorObject
  2738. */ /**
  2739. * Start horizontal position of the gradient. Float ranges 0-1.
  2740. * @name Highcharts.LinearGradientColorObject#x1
  2741. * @type {number}
  2742. */ /**
  2743. * End horizontal position of the gradient. Float ranges 0-1.
  2744. * @name Highcharts.LinearGradientColorObject#x2
  2745. * @type {number}
  2746. */ /**
  2747. * Start vertical position of the gradient. Float ranges 0-1.
  2748. * @name Highcharts.LinearGradientColorObject#y1
  2749. * @type {number}
  2750. */ /**
  2751. * End vertical position of the gradient. Float ranges 0-1.
  2752. * @name Highcharts.LinearGradientColorObject#y2
  2753. * @type {number}
  2754. */
  2755. /**
  2756. * Defines the center position and the radius for a gradient.
  2757. *
  2758. * @interface Highcharts.RadialGradientColorObject
  2759. */ /**
  2760. * Center horizontal position relative to the shape. Float ranges 0-1.
  2761. * @name Highcharts.RadialGradientColorObject#cx
  2762. * @type {number}
  2763. */ /**
  2764. * Center vertical position relative to the shape. Float ranges 0-1.
  2765. * @name Highcharts.RadialGradientColorObject#cy
  2766. * @type {number}
  2767. */ /**
  2768. * Radius relative to the shape. Float ranges 0-1.
  2769. * @name Highcharts.RadialGradientColorObject#r
  2770. * @type {number}
  2771. */
  2772. var isNumber = U.isNumber, pInt = U.pInt;
  2773. var merge = H.merge;
  2774. /* eslint-disable no-invalid-this, valid-jsdoc */
  2775. /**
  2776. * Handle color operations. Some object methods are chainable.
  2777. *
  2778. * @class
  2779. * @name Highcharts.Color
  2780. *
  2781. * @param {Highcharts.ColorType} input
  2782. * The input color in either rbga or hex format
  2783. */
  2784. H.Color = function (input) {
  2785. // Backwards compatibility, allow instanciation without new
  2786. if (!(this instanceof H.Color)) {
  2787. return new H.Color(input);
  2788. }
  2789. // Initialize
  2790. this.init(input);
  2791. };
  2792. H.Color.prototype = {
  2793. // Collection of parsers. This can be extended from the outside by pushing
  2794. // parsers to Highcharts.Color.prototype.parsers.
  2795. parsers: [{
  2796. // RGBA color
  2797. // eslint-disable-next-line max-len
  2798. 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*\)/,
  2799. parse: function (result) {
  2800. return [
  2801. pInt(result[1]),
  2802. pInt(result[2]),
  2803. pInt(result[3]),
  2804. parseFloat(result[4], 10)
  2805. ];
  2806. }
  2807. }, {
  2808. // RGB color
  2809. regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  2810. parse: function (result) {
  2811. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  2812. }
  2813. }],
  2814. // Collection of named colors. Can be extended from the outside by adding
  2815. // colors to Highcharts.Color.prototype.names.
  2816. names: {
  2817. white: '#ffffff',
  2818. black: '#000000'
  2819. },
  2820. /**
  2821. * Parse the input color to rgba array
  2822. *
  2823. * @private
  2824. * @function Highcharts.Color#init
  2825. *
  2826. * @param {Highcharts.ColorType} input
  2827. * The input color in either rbga or hex format
  2828. *
  2829. * @return {void}
  2830. */
  2831. init: function (input) {
  2832. var result, rgba, i, parser, len;
  2833. this.input = input = this.names[input && input.toLowerCase ?
  2834. input.toLowerCase() :
  2835. ''] || input;
  2836. // Gradients
  2837. if (input && input.stops) {
  2838. this.stops = input.stops.map(function (stop) {
  2839. return new H.Color(stop[1]);
  2840. });
  2841. // Solid colors
  2842. }
  2843. else {
  2844. // Bitmasking as input[0] is not working for legacy IE.
  2845. if (input &&
  2846. input.charAt &&
  2847. input.charAt() === '#') {
  2848. len = input.length;
  2849. input = parseInt(input.substr(1), 16);
  2850. // Handle long-form, e.g. #AABBCC
  2851. if (len === 7) {
  2852. rgba = [
  2853. (input & 0xFF0000) >> 16,
  2854. (input & 0xFF00) >> 8,
  2855. (input & 0xFF),
  2856. 1
  2857. ];
  2858. // Handle short-form, e.g. #ABC
  2859. // In short form, the value is assumed to be the same
  2860. // for both nibbles for each component. e.g. #ABC = #AABBCC
  2861. }
  2862. else if (len === 4) {
  2863. rgba = [
  2864. (((input & 0xF00) >> 4) |
  2865. (input & 0xF00) >> 8),
  2866. (((input & 0xF0) >> 4) |
  2867. (input & 0xF0)),
  2868. ((input & 0xF) << 4) | (input & 0xF),
  2869. 1
  2870. ];
  2871. }
  2872. }
  2873. // Otherwise, check regex parsers
  2874. if (!rgba) {
  2875. i = this.parsers.length;
  2876. while (i-- && !rgba) {
  2877. parser = this.parsers[i];
  2878. result = parser.regex.exec(input);
  2879. if (result) {
  2880. rgba = parser.parse(result);
  2881. }
  2882. }
  2883. }
  2884. }
  2885. this.rgba = rgba || [];
  2886. },
  2887. /**
  2888. * Return the color or gradient stops in the specified format
  2889. *
  2890. * @function Highcharts.Color#get
  2891. *
  2892. * @param {string} [format]
  2893. * Possible values are 'a', 'rgb', 'rgba' (default).
  2894. *
  2895. * @return {Highcharts.ColorType}
  2896. * This color as a string or gradient stops.
  2897. */
  2898. get: function (format) {
  2899. var input = this.input, rgba = this.rgba, ret;
  2900. if (this.stops) {
  2901. ret = merge(input);
  2902. ret.stops = [].concat(ret.stops);
  2903. this.stops.forEach(function (stop, i) {
  2904. ret.stops[i] = [
  2905. ret.stops[i][0],
  2906. stop.get(format)
  2907. ];
  2908. });
  2909. // it's NaN if gradient colors on a column chart
  2910. }
  2911. else if (rgba && isNumber(rgba[0])) {
  2912. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  2913. ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  2914. }
  2915. else if (format === 'a') {
  2916. ret = rgba[3];
  2917. }
  2918. else {
  2919. ret = 'rgba(' + rgba.join(',') + ')';
  2920. }
  2921. }
  2922. else {
  2923. ret = input;
  2924. }
  2925. return ret;
  2926. },
  2927. /**
  2928. * Brighten the color instance.
  2929. *
  2930. * @function Highcharts.Color#brighten
  2931. *
  2932. * @param {number} alpha
  2933. * The alpha value.
  2934. *
  2935. * @return {Highcharts.Color}
  2936. * This color with modifications.
  2937. */
  2938. brighten: function (alpha) {
  2939. var i, rgba = this.rgba;
  2940. if (this.stops) {
  2941. this.stops.forEach(function (stop) {
  2942. stop.brighten(alpha);
  2943. });
  2944. }
  2945. else if (isNumber(alpha) && alpha !== 0) {
  2946. for (i = 0; i < 3; i++) {
  2947. rgba[i] += pInt(alpha * 255);
  2948. if (rgba[i] < 0) {
  2949. rgba[i] = 0;
  2950. }
  2951. if (rgba[i] > 255) {
  2952. rgba[i] = 255;
  2953. }
  2954. }
  2955. }
  2956. return this;
  2957. },
  2958. /**
  2959. * Set the color's opacity to a given alpha value.
  2960. *
  2961. * @function Highcharts.Color#setOpacity
  2962. *
  2963. * @param {number} alpha
  2964. * Opacity between 0 and 1.
  2965. *
  2966. * @return {Highcharts.Color}
  2967. * Color with modifications.
  2968. */
  2969. setOpacity: function (alpha) {
  2970. this.rgba[3] = alpha;
  2971. return this;
  2972. },
  2973. /**
  2974. * Return an intermediate color between two colors.
  2975. *
  2976. * @function Highcharts.Color#tweenTo
  2977. *
  2978. * @param {Highcharts.Color} to
  2979. * The color object to tween to.
  2980. *
  2981. * @param {number} pos
  2982. * The intermediate position, where 0 is the from color (current
  2983. * color item), and 1 is the `to` color.
  2984. *
  2985. * @return {Highcharts.ColorString}
  2986. * The intermediate color in rgba notation.
  2987. */
  2988. tweenTo: function (to, pos) {
  2989. // Check for has alpha, because rgba colors perform worse due to lack of
  2990. // support in WebKit.
  2991. var fromRgba = this.rgba, toRgba = to.rgba, hasAlpha, ret;
  2992. // Unsupported color, return to-color (#3920, #7034)
  2993. if (!toRgba.length || !fromRgba || !fromRgba.length) {
  2994. ret = to.input || 'none';
  2995. // Interpolate
  2996. }
  2997. else {
  2998. hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  2999. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  3000. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  3001. ',' +
  3002. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  3003. ',' +
  3004. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  3005. (hasAlpha ?
  3006. (',' +
  3007. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
  3008. '') +
  3009. ')';
  3010. }
  3011. return ret;
  3012. }
  3013. };
  3014. /**
  3015. * Creates a color instance out of a color string.
  3016. *
  3017. * @function Highcharts.color
  3018. *
  3019. * @param {Highcharts.ColorType} input
  3020. * The input color in either rbga or hex format
  3021. *
  3022. * @return {Highcharts.Color}
  3023. * Color instance
  3024. */
  3025. H.color = function (input) {
  3026. return new H.Color(input);
  3027. };
  3028. });
  3029. _registerModule(_modules, 'parts/SvgRenderer.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  3030. /* *
  3031. *
  3032. * (c) 2010-2019 Torstein Honsi
  3033. *
  3034. * License: www.highcharts.com/license
  3035. *
  3036. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3037. *
  3038. * */
  3039. /**
  3040. * The horizontal alignment of an element.
  3041. *
  3042. * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
  3043. */
  3044. /**
  3045. * Options to align the element relative to the chart or another box.
  3046. *
  3047. * @interface Highcharts.AlignObject
  3048. */ /**
  3049. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  3050. *
  3051. * @name Highcharts.AlignObject#align
  3052. * @type {Highcharts.AlignValue|undefined}
  3053. *
  3054. * @default left
  3055. */ /**
  3056. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  3057. *
  3058. * @name Highcharts.AlignObject#verticalAlign
  3059. * @type {Highcharts.VerticalAlignValue|undefined}
  3060. *
  3061. * @default top
  3062. */ /**
  3063. * Horizontal pixel offset from alignment.
  3064. *
  3065. * @name Highcharts.AlignObject#x
  3066. * @type {number|undefined}
  3067. *
  3068. * @default 0
  3069. */ /**
  3070. * Vertical pixel offset from alignment.
  3071. *
  3072. * @name Highcharts.AlignObject#y
  3073. * @type {number|undefined}
  3074. *
  3075. * @default 0
  3076. */ /**
  3077. * Use the `transform` attribute with translateX and translateY custom
  3078. * attributes to align this elements rather than `x` and `y` attributes.
  3079. *
  3080. * @name Highcharts.AlignObject#alignByTranslate
  3081. * @type {boolean|undefined}
  3082. *
  3083. * @default false
  3084. */
  3085. /**
  3086. * Bounding box of an element.
  3087. *
  3088. * @interface Highcharts.BBoxObject
  3089. * @extends Highcharts.PositionObject
  3090. */ /**
  3091. * Height of the bounding box.
  3092. *
  3093. * @name Highcharts.BBoxObject#height
  3094. * @type {number}
  3095. */ /**
  3096. * Width of the bounding box.
  3097. *
  3098. * @name Highcharts.BBoxObject#width
  3099. * @type {number}
  3100. */ /**
  3101. * Horizontal position of the bounding box.
  3102. *
  3103. * @name Highcharts.BBoxObject#x
  3104. * @type {number}
  3105. */ /**
  3106. * Vertical position of the bounding box.
  3107. *
  3108. * @name Highcharts.BBoxObject#y
  3109. * @type {number}
  3110. */
  3111. /**
  3112. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  3113. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  3114. * and applied with the {@link SVGElement#clip} function.
  3115. *
  3116. * @example
  3117. * var circle = renderer.circle(100, 100, 100)
  3118. * .attr({ fill: 'red' })
  3119. * .add();
  3120. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  3121. *
  3122. * // Leave only the lower right quarter visible
  3123. * circle.clip(clipRect);
  3124. *
  3125. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  3126. */
  3127. /**
  3128. * The font metrics.
  3129. *
  3130. * @interface Highcharts.FontMetricsObject
  3131. */ /**
  3132. * The baseline relative to the top of the box.
  3133. *
  3134. * @name Highcharts.FontMetricsObject#b
  3135. * @type {number}
  3136. */ /**
  3137. * The font size.
  3138. *
  3139. * @name Highcharts.FontMetricsObject#f
  3140. * @type {number}
  3141. */ /**
  3142. * The line height.
  3143. *
  3144. * @name Highcharts.FontMetricsObject#h
  3145. * @type {number}
  3146. */
  3147. /**
  3148. * An object containing `x` and `y` properties for the position of an element.
  3149. *
  3150. * @interface Highcharts.PositionObject
  3151. */ /**
  3152. * X position of the element.
  3153. * @name Highcharts.PositionObject#x
  3154. * @type {number}
  3155. */ /**
  3156. * Y position of the element.
  3157. * @name Highcharts.PositionObject#y
  3158. * @type {number}
  3159. */
  3160. /**
  3161. * A rectangle.
  3162. *
  3163. * @interface Highcharts.RectangleObject
  3164. */ /**
  3165. * Height of the rectangle.
  3166. * @name Highcharts.RectangleObject#height
  3167. * @type {number}
  3168. */ /**
  3169. * Width of the rectangle.
  3170. * @name Highcharts.RectangleObject#width
  3171. * @type {number}
  3172. */ /**
  3173. * Horizontal position of the rectangle.
  3174. * @name Highcharts.RectangleObject#x
  3175. * @type {number}
  3176. */ /**
  3177. * Vertical position of the rectangle.
  3178. * @name Highcharts.RectangleObject#y
  3179. * @type {number}
  3180. */
  3181. /**
  3182. * The shadow options.
  3183. *
  3184. * @interface Highcharts.ShadowOptionsObject
  3185. */ /**
  3186. * The shadow color.
  3187. * @name Highcharts.ShadowOptionsObject#color
  3188. * @type {Highcharts.ColorString|undefined}
  3189. * @default #000000
  3190. */ /**
  3191. * The horizontal offset from the element.
  3192. *
  3193. * @name Highcharts.ShadowOptionsObject#offsetX
  3194. * @type {number|undefined}
  3195. * @default 1
  3196. */ /**
  3197. * The vertical offset from the element.
  3198. * @name Highcharts.ShadowOptionsObject#offsetY
  3199. * @type {number|undefined}
  3200. * @default 1
  3201. */ /**
  3202. * The shadow opacity.
  3203. *
  3204. * @name Highcharts.ShadowOptionsObject#opacity
  3205. * @type {number|undefined}
  3206. * @default 0.15
  3207. */ /**
  3208. * The shadow width or distance from the element.
  3209. * @name Highcharts.ShadowOptionsObject#width
  3210. * @type {number|undefined}
  3211. * @default 3
  3212. */
  3213. /**
  3214. * @interface Highcharts.SizeObject
  3215. */ /**
  3216. * @name Highcharts.SizeObject#height
  3217. * @type {number}
  3218. */ /**
  3219. * @name Highcharts.SizeObject#width
  3220. * @type {number}
  3221. */
  3222. /**
  3223. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  3224. * elements for the most parts correspond to SVG, but some are specific to
  3225. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  3226. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  3227. * attributes containing a hyphen are _not_ camel-cased, they should be
  3228. * quoted to preserve the hyphen.
  3229. *
  3230. * @example
  3231. * {
  3232. * 'stroke': '#ff0000', // basic
  3233. * 'stroke-width': 2, // hyphenated
  3234. * 'rotation': 45 // custom
  3235. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  3236. * }
  3237. *
  3238. * @interface Highcharts.SVGAttributes
  3239. */ /**
  3240. * @name Highcharts.SVGAttributes#[key:string]
  3241. * @type {*}
  3242. */ /**
  3243. * @name Highcharts.SVGAttributes#d
  3244. * @type {string|Highcharts.SVGPathArray|undefined}
  3245. */ /**
  3246. * @name Highcharts.SVGAttributes#fill
  3247. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3248. */ /**
  3249. * @name Highcharts.SVGAttributes#inverted
  3250. * @type {boolean|undefined}
  3251. */ /**
  3252. * @name Highcharts.SVGAttributes#matrix
  3253. * @type {Array<number>|undefined}
  3254. */ /**
  3255. * @name Highcharts.SVGAttributes#rotation
  3256. * @type {number|undefined}
  3257. */ /**
  3258. * @name Highcharts.SVGAttributes#rotationOriginX
  3259. * @type {number|undefined}
  3260. */ /**
  3261. * @name Highcharts.SVGAttributes#rotationOriginY
  3262. * @type {number|undefined}
  3263. */ /**
  3264. * @name Highcharts.SVGAttributes#scaleX
  3265. * @type {number|undefined}
  3266. */ /**
  3267. * @name Highcharts.SVGAttributes#scaleY
  3268. * @type {number|undefined}
  3269. */ /**
  3270. * @name Highcharts.SVGAttributes#stroke
  3271. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3272. */ /**
  3273. * @name Highcharts.SVGAttributes#style
  3274. * @type {string|Highcharts.CSSObject|undefined}
  3275. */ /**
  3276. * @name Highcharts.SVGAttributes#translateX
  3277. * @type {number|undefined}
  3278. */ /**
  3279. * @name Highcharts.SVGAttributes#translateY
  3280. * @type {number|undefined}
  3281. */ /**
  3282. * @name Highcharts.SVGAttributes#zIndex
  3283. * @type {number|undefined}
  3284. */
  3285. /**
  3286. * Serialized form of an SVG definition, including children. Some key
  3287. * property names are reserved: tagName, textContent, and children.
  3288. *
  3289. * @interface Highcharts.SVGDefinitionObject
  3290. */ /**
  3291. * @name Highcharts.SVGDefinitionObject#[key:string]
  3292. * @type {number|string|Array<Highcharts.SVGDefinitionObject>|undefined}
  3293. */ /**
  3294. * @name Highcharts.SVGDefinitionObject#children
  3295. * @type {Array<Highcharts.SVGDefinitionObject>|undefined}
  3296. */ /**
  3297. * @name Highcharts.SVGDefinitionObject#tagName
  3298. * @type {string|undefined}
  3299. */ /**
  3300. * @name Highcharts.SVGDefinitionObject#textContent
  3301. * @type {string|undefined}
  3302. */
  3303. /**
  3304. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  3305. * global scope.
  3306. *
  3307. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  3308. *
  3309. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  3310. */
  3311. /**
  3312. * Array of path commands, that will go into the `d` attribute of an SVG
  3313. * element.
  3314. *
  3315. * @typedef {Array<number|Highcharts.SVGPathCommand>} Highcharts.SVGPathArray
  3316. */
  3317. /**
  3318. * Possible path commands in a SVG path array.
  3319. *
  3320. * @typedef {string} Highcharts.SVGPathCommand
  3321. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  3322. */
  3323. /**
  3324. * An extendable collection of functions for defining symbol paths. Symbols are
  3325. * used internally for point markers, button and label borders and backgrounds,
  3326. * or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
  3327. *
  3328. * @interface Highcharts.SymbolDictionary
  3329. */ /**
  3330. * @name Highcharts.SymbolDictionary#[key:string]
  3331. * @type {Function|undefined}
  3332. */ /**
  3333. * @name Highcharts.SymbolDictionary#arc
  3334. * @type {Function|undefined}
  3335. */ /**
  3336. * @name Highcharts.SymbolDictionary#callout
  3337. * @type {Function|undefined}
  3338. */ /**
  3339. * @name Highcharts.SymbolDictionary#circle
  3340. * @type {Function|undefined}
  3341. */ /**
  3342. * @name Highcharts.SymbolDictionary#diamond
  3343. * @type {Function|undefined}
  3344. */ /**
  3345. * @name Highcharts.SymbolDictionary#square
  3346. * @type {Function|undefined}
  3347. */ /**
  3348. * @name Highcharts.SymbolDictionary#triangle
  3349. * @type {Function|undefined}
  3350. */
  3351. /**
  3352. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
  3353. * and `triangle-down`. Symbols are used internally for point markers, button
  3354. * and label borders and backgrounds, or custom shapes. Extendable by adding to
  3355. * {@link SVGRenderer#symbols}.
  3356. *
  3357. * @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
  3358. */
  3359. /**
  3360. * Additional options, depending on the actual symbol drawn.
  3361. *
  3362. * @interface Highcharts.SymbolOptionsObject
  3363. */ /**
  3364. * The anchor X position for the `callout` symbol. This is where the chevron
  3365. * points to.
  3366. *
  3367. * @name Highcharts.SymbolOptionsObject#anchorX
  3368. * @type {number|undefined}
  3369. */ /**
  3370. * The anchor Y position for the `callout` symbol. This is where the chevron
  3371. * points to.
  3372. *
  3373. * @name Highcharts.SymbolOptionsObject#anchorY
  3374. * @type {number|undefined}
  3375. */ /**
  3376. * The end angle of an `arc` symbol.
  3377. *
  3378. * @name Highcharts.SymbolOptionsObject#end
  3379. * @type {number|undefined}
  3380. */ /**
  3381. * Whether to draw `arc` symbol open or closed.
  3382. *
  3383. * @name Highcharts.SymbolOptionsObject#open
  3384. * @type {boolean|undefined}
  3385. */ /**
  3386. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  3387. *
  3388. * @name Highcharts.SymbolOptionsObject#r
  3389. * @type {number|undefined}
  3390. */ /**
  3391. * The start angle of an `arc` symbol.
  3392. *
  3393. * @name Highcharts.SymbolOptionsObject#start
  3394. * @type {number|undefined}
  3395. */
  3396. /**
  3397. * The vertical alignment of an element.
  3398. *
  3399. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
  3400. */
  3401. /* eslint-disable no-invalid-this, valid-jsdoc */
  3402. var animObject = U.animObject, attr = U.attr, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, splat = U.splat;
  3403. var SVGElement, SVGRenderer, addEvent = H.addEvent, animate = H.animate, charts = H.charts, color = H.color, css = H.css, createElement = H.createElement, deg2rad = H.deg2rad, doc = H.doc, hasTouch = H.hasTouch, isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, merge = H.merge, noop = H.noop, removeEvent = H.removeEvent, stop = H.stop, svg = H.svg, SVG_NS = H.SVG_NS, symbolSizes = H.symbolSizes, win = H.win;
  3404. /**
  3405. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  3406. * rendering layer of Highcharts. Combined with the {@link
  3407. * Highcharts.SVGRenderer} object, these prototypes allow freeform annotation
  3408. * in the charts or even in HTML pages without instanciating a chart. The
  3409. * SVGElement can also wrap HTML labels, when `text` or `label` elements are
  3410. * created with the `useHTML` parameter.
  3411. *
  3412. * The SVGElement instances are created through factory functions on the {@link
  3413. * Highcharts.SVGRenderer} object, like {@link Highcharts.SVGRenderer#rect|
  3414. * rect}, {@link Highcharts.SVGRenderer#path|path}, {@link
  3415. * Highcharts.SVGRenderer#text|text}, {@link Highcharts.SVGRenderer#label|
  3416. * label}, {@link Highcharts.SVGRenderer#g|g} and more.
  3417. *
  3418. * @class
  3419. * @name Highcharts.SVGElement
  3420. */
  3421. SVGElement = H.SVGElement = function () {
  3422. return this;
  3423. };
  3424. extend(SVGElement.prototype, /** @lends Highcharts.SVGElement.prototype */ {
  3425. // Default base for animation
  3426. opacity: 1,
  3427. SVG_NS: SVG_NS,
  3428. /**
  3429. * For labels, these CSS properties are applied to the `text` node directly.
  3430. *
  3431. * @private
  3432. * @name Highcharts.SVGElement#textProps
  3433. * @type {Array<string>}
  3434. */
  3435. textProps: ['direction', 'fontSize', 'fontWeight', 'fontFamily',
  3436. 'fontStyle', 'color', 'lineHeight', 'width', 'textAlign',
  3437. 'textDecoration', 'textOverflow', 'textOutline', 'cursor'],
  3438. /**
  3439. * Initialize the SVG element. This function only exists to make the
  3440. * initialization process overridable. It should not be called directly.
  3441. *
  3442. * @function Highcharts.SVGElement#init
  3443. *
  3444. * @param {Highcharts.SVGRenderer} renderer
  3445. * The SVGRenderer instance to initialize to.
  3446. *
  3447. * @param {string} nodeName
  3448. * The SVG node name.
  3449. *
  3450. * @return {void}
  3451. */
  3452. init: function (renderer, nodeName) {
  3453. /**
  3454. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  3455. * node, but may also represent more nodes.
  3456. *
  3457. * @name Highcharts.SVGElement#element
  3458. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  3459. */
  3460. this.element = nodeName === 'span' ?
  3461. createElement(nodeName) :
  3462. doc.createElementNS(this.SVG_NS, nodeName);
  3463. /**
  3464. * The renderer that the SVGElement belongs to.
  3465. *
  3466. * @name Highcharts.SVGElement#renderer
  3467. * @type {Highcharts.SVGRenderer}
  3468. */
  3469. this.renderer = renderer;
  3470. H.fireEvent(this, 'afterInit');
  3471. },
  3472. /**
  3473. * Animate to given attributes or CSS properties.
  3474. *
  3475. * @sample highcharts/members/element-on/
  3476. * Setting some attributes by animation
  3477. *
  3478. * @function Highcharts.SVGElement#animate
  3479. *
  3480. * @param {Highcharts.SVGAttributes} params
  3481. * SVG attributes or CSS to animate.
  3482. *
  3483. * @param {boolean|Highcharts.AnimationOptionsObject} [options]
  3484. * Animation options.
  3485. *
  3486. * @param {Function} [complete]
  3487. * Function to perform at the end of animation.
  3488. *
  3489. * @return {Highcharts.SVGElement}
  3490. * Returns the SVGElement for chaining.
  3491. */
  3492. animate: function (params, options, complete) {
  3493. var animOptions = animObject(pick(options, this.renderer.globalAnimation, true));
  3494. // When the page is hidden save resources in the background by not
  3495. // running animation at all (#9749).
  3496. if (pick(doc.hidden, doc.msHidden, doc.webkitHidden, false)) {
  3497. animOptions.duration = 0;
  3498. }
  3499. if (animOptions.duration !== 0) {
  3500. // allows using a callback with the global animation without
  3501. // overwriting it
  3502. if (complete) {
  3503. animOptions.complete = complete;
  3504. }
  3505. animate(this, params, animOptions);
  3506. }
  3507. else {
  3508. this.attr(params, void 0, complete);
  3509. // Call the end step synchronously
  3510. objectEach(params, function (val, prop) {
  3511. if (animOptions.step) {
  3512. animOptions.step.call(this, val, { prop: prop, pos: 1 });
  3513. }
  3514. }, this);
  3515. }
  3516. return this;
  3517. },
  3518. /**
  3519. * Build and apply an SVG gradient out of a common JavaScript configuration
  3520. * object. This function is called from the attribute setters. An event
  3521. * hook is added for supporting other complex color types.
  3522. *
  3523. * @private
  3524. * @function Highcharts.SVGElement#complexColor
  3525. *
  3526. * @param {Highcharts.GradientColorObject} color
  3527. * The gradient options structure.
  3528. *
  3529. * @param {string} prop
  3530. * The property to apply, can either be `fill` or `stroke`.
  3531. *
  3532. * @param {Highcharts.SVGDOMElement} elem
  3533. * SVG element to apply the gradient on.
  3534. *
  3535. * @return {void}
  3536. */
  3537. complexColor: function (color, prop, elem) {
  3538. var renderer = this.renderer, colorObject, gradName, gradAttr, radAttr, gradients, gradientObject, stops, stopColor, stopOpacity, radialReference, id, key = [], value;
  3539. H.fireEvent(this.renderer, 'complexColor', {
  3540. args: arguments
  3541. }, function () {
  3542. // Apply linear or radial gradients
  3543. if (color.radialGradient) {
  3544. gradName = 'radialGradient';
  3545. }
  3546. else if (color.linearGradient) {
  3547. gradName = 'linearGradient';
  3548. }
  3549. if (gradName) {
  3550. gradAttr = color[gradName];
  3551. gradients = renderer.gradients;
  3552. stops = color.stops;
  3553. radialReference = elem.radialReference;
  3554. // Keep < 2.2 kompatibility
  3555. if (isArray(gradAttr)) {
  3556. color[gradName] = gradAttr = {
  3557. x1: gradAttr[0],
  3558. y1: gradAttr[1],
  3559. x2: gradAttr[2],
  3560. y2: gradAttr[3],
  3561. gradientUnits: 'userSpaceOnUse'
  3562. };
  3563. }
  3564. // Correct the radial gradient for the radial reference system
  3565. if (gradName === 'radialGradient' &&
  3566. radialReference &&
  3567. !defined(gradAttr.gradientUnits)) {
  3568. // Save the radial attributes for updating
  3569. radAttr = gradAttr;
  3570. gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
  3571. }
  3572. // Build the unique key to detect whether we need to create a
  3573. // new element (#1282)
  3574. objectEach(gradAttr, function (val, n) {
  3575. if (n !== 'id') {
  3576. key.push(n, val);
  3577. }
  3578. });
  3579. objectEach(stops, function (val) {
  3580. key.push(val);
  3581. });
  3582. key = key.join(',');
  3583. // Check if a gradient object with the same config object is
  3584. // created within this renderer
  3585. if (gradients[key]) {
  3586. id = gradients[key].attr('id');
  3587. }
  3588. else {
  3589. // Set the id and create the element
  3590. gradAttr.id = id = H.uniqueKey();
  3591. gradients[key] = gradientObject =
  3592. renderer.createElement(gradName)
  3593. .attr(gradAttr)
  3594. .add(renderer.defs);
  3595. gradientObject.radAttr = radAttr;
  3596. // The gradient needs to keep a list of stops to be able to
  3597. // destroy them
  3598. gradientObject.stops = [];
  3599. stops.forEach(function (stop) {
  3600. var stopObject;
  3601. if (stop[1].indexOf('rgba') === 0) {
  3602. colorObject = H.color(stop[1]);
  3603. stopColor = colorObject.get('rgb');
  3604. stopOpacity = colorObject.get('a');
  3605. }
  3606. else {
  3607. stopColor = stop[1];
  3608. stopOpacity = 1;
  3609. }
  3610. stopObject = renderer.createElement('stop').attr({
  3611. offset: stop[0],
  3612. 'stop-color': stopColor,
  3613. 'stop-opacity': stopOpacity
  3614. }).add(gradientObject);
  3615. // Add the stop element to the gradient
  3616. gradientObject.stops.push(stopObject);
  3617. });
  3618. }
  3619. // Set the reference to the gradient object
  3620. value = 'url(' + renderer.url + '#' + id + ')';
  3621. elem.setAttribute(prop, value);
  3622. elem.gradient = key;
  3623. // Allow the color to be concatenated into tooltips formatters
  3624. // etc. (#2995)
  3625. color.toString = function () {
  3626. return value;
  3627. };
  3628. }
  3629. });
  3630. },
  3631. /**
  3632. * Apply a text outline through a custom CSS property, by copying the text
  3633. * element and apply stroke to the copy. Used internally. Contrast checks at
  3634. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  3635. *
  3636. * @example
  3637. * // Specific color
  3638. * text.css({
  3639. * textOutline: '1px black'
  3640. * });
  3641. * // Automatic contrast
  3642. * text.css({
  3643. * color: '#000000', // black text
  3644. * textOutline: '1px contrast' // => white outline
  3645. * });
  3646. *
  3647. * @private
  3648. * @function Highcharts.SVGElement#applyTextOutline
  3649. *
  3650. * @param {string} textOutline
  3651. * A custom CSS `text-outline` setting, defined by `width color`.
  3652. *
  3653. * @return {void}
  3654. */
  3655. applyTextOutline: function (textOutline) {
  3656. var elem = this.element, tspans, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {}, color, strokeWidth, firstRealChild;
  3657. // When the text shadow is set to contrast, use dark stroke for light
  3658. // text and vice versa.
  3659. if (hasContrast) {
  3660. styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
  3661. }
  3662. // Extract the stroke width and color
  3663. textOutline = textOutline.split(' ');
  3664. color = textOutline[textOutline.length - 1];
  3665. strokeWidth = textOutline[0];
  3666. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  3667. this.fakeTS = true; // Fake text shadow
  3668. tspans = [].slice.call(elem.getElementsByTagName('tspan'));
  3669. // In order to get the right y position of the clone,
  3670. // copy over the y setter
  3671. this.ySetter = this.xSetter;
  3672. // Since the stroke is applied on center of the actual outline, we
  3673. // need to double it to get the correct stroke-width outside the
  3674. // glyphs.
  3675. strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
  3676. return (2 * digit) + unit;
  3677. });
  3678. // Remove shadows from previous runs.
  3679. this.removeTextOutline(tspans);
  3680. // For each of the tspans, create a stroked copy behind it.
  3681. firstRealChild = elem.firstChild;
  3682. tspans.forEach(function (tspan, y) {
  3683. var clone;
  3684. // Let the first line start at the correct X position
  3685. if (y === 0) {
  3686. tspan.setAttribute('x', elem.getAttribute('x'));
  3687. y = elem.getAttribute('y');
  3688. tspan.setAttribute('y', y || 0);
  3689. if (y === null) {
  3690. elem.setAttribute('y', 0);
  3691. }
  3692. }
  3693. // Create the clone and apply outline properties
  3694. clone = tspan.cloneNode(1);
  3695. attr(clone, {
  3696. 'class': 'highcharts-text-outline',
  3697. fill: color,
  3698. stroke: color,
  3699. 'stroke-width': strokeWidth,
  3700. 'stroke-linejoin': 'round'
  3701. });
  3702. elem.insertBefore(clone, firstRealChild);
  3703. });
  3704. }
  3705. },
  3706. /**
  3707. * @private
  3708. * @param {Array<Highcharts.SVGDOMElement>} tspans - text spans
  3709. * @return {void}
  3710. */
  3711. removeTextOutline: function (tspans) {
  3712. // Iterate from the end to
  3713. // support removing items inside the cycle (#6472).
  3714. var i = tspans.length, tspan;
  3715. while (i--) {
  3716. tspan = tspans[i];
  3717. if (tspan.getAttribute('class') === 'highcharts-text-outline') {
  3718. // Remove then erase
  3719. erase(tspans, this.element.removeChild(tspan));
  3720. }
  3721. }
  3722. },
  3723. // Custom attributes used for symbols, these should be filtered out when
  3724. // setting SVGElement attributes (#9375).
  3725. symbolCustomAttribs: [
  3726. 'x',
  3727. 'y',
  3728. 'width',
  3729. 'height',
  3730. 'r',
  3731. 'start',
  3732. 'end',
  3733. 'innerR',
  3734. 'anchorX',
  3735. 'anchorY',
  3736. 'rounded'
  3737. ],
  3738. /**
  3739. * @function Highcharts.SVGElement#attr
  3740. *
  3741. * @param {string} hash
  3742. *
  3743. * @return {Highcharts.SVGElement}
  3744. */ /**
  3745. * Apply native and custom attributes to the SVG elements.
  3746. *
  3747. * In order to set the rotation center for rotation, set x and y to 0 and
  3748. * use `translateX` and `translateY` attributes to position the element
  3749. * instead.
  3750. *
  3751. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  3752. * `stroke-width`.
  3753. *
  3754. * @sample highcharts/members/renderer-rect/
  3755. * Setting some attributes
  3756. *
  3757. * @example
  3758. * // Set multiple attributes
  3759. * element.attr({
  3760. * stroke: 'red',
  3761. * fill: 'blue',
  3762. * x: 10,
  3763. * y: 10
  3764. * });
  3765. *
  3766. * // Set a single attribute
  3767. * element.attr('stroke', 'red');
  3768. *
  3769. * // Get an attribute
  3770. * element.attr('stroke'); // => 'red'
  3771. *
  3772. * @function Highcharts.SVGElement#attr
  3773. *
  3774. * @param {string|Highcharts.SVGAttributes} [hash]
  3775. * The native and custom SVG attributes.
  3776. *
  3777. * @param {string} [val]
  3778. * If the type of the first argument is `string`, the second can be a
  3779. * value, which will serve as a single attribute setter. If the first
  3780. * argument is a string and the second is undefined, the function
  3781. * serves as a getter and the current value of the property is
  3782. * returned.
  3783. *
  3784. * @param {Function} [complete]
  3785. * A callback function to execute after setting the attributes. This
  3786. * makes the function compliant and interchangeable with the
  3787. * {@link SVGElement#animate} function.
  3788. *
  3789. * @param {boolean} [continueAnimation=true]
  3790. * Used internally when `.attr` is called as part of an animation
  3791. * step. Otherwise, calling `.attr` for an attribute will stop
  3792. * animation for that attribute.
  3793. *
  3794. * @return {Highcharts.SVGElement}
  3795. * If used as a setter, it returns the current
  3796. * {@link Highcharts.SVGElement} so the calls can be chained. If
  3797. * used as a getter, the current value of the attribute is returned.
  3798. */
  3799. attr: function (hash, val, complete, continueAnimation) {
  3800. var key, element = this.element, hasSetSymbolSize, ret = this, skipAttr, setter, symbolCustomAttribs = this.symbolCustomAttribs;
  3801. // single key-value pair
  3802. if (typeof hash === 'string' && typeof val !== 'undefined') {
  3803. key = hash;
  3804. hash = {};
  3805. hash[key] = val;
  3806. }
  3807. // used as a getter: first argument is a string, second is undefined
  3808. if (typeof hash === 'string') {
  3809. ret = (this[hash + 'Getter'] || this._defaultGetter).call(this, hash, element);
  3810. // setter
  3811. }
  3812. else {
  3813. objectEach(hash, function eachAttribute(val, key) {
  3814. skipAttr = false;
  3815. // Unless .attr is from the animator update, stop current
  3816. // running animation of this property
  3817. if (!continueAnimation) {
  3818. stop(this, key);
  3819. }
  3820. // Special handling of symbol attributes
  3821. if (this.symbolName &&
  3822. H.inArray(key, symbolCustomAttribs) !== -1) {
  3823. if (!hasSetSymbolSize) {
  3824. this.symbolAttr(hash);
  3825. hasSetSymbolSize = true;
  3826. }
  3827. skipAttr = true;
  3828. }
  3829. if (this.rotation && (key === 'x' || key === 'y')) {
  3830. this.doTransform = true;
  3831. }
  3832. if (!skipAttr) {
  3833. setter = this[key + 'Setter'] || this._defaultSetter;
  3834. setter.call(this, val, key, element);
  3835. // Let the shadow follow the main element
  3836. if (!this.styledMode &&
  3837. this.shadows &&
  3838. /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/
  3839. .test(key)) {
  3840. this.updateShadows(key, val, setter);
  3841. }
  3842. }
  3843. }, this);
  3844. this.afterSetters();
  3845. }
  3846. // In accordance with animate, run a complete callback
  3847. if (complete) {
  3848. complete.call(this);
  3849. }
  3850. return ret;
  3851. },
  3852. /**
  3853. * This method is executed in the end of `attr()`, after setting all
  3854. * attributes in the hash. In can be used to efficiently consolidate
  3855. * multiple attributes in one SVG property -- e.g., translate, rotate and
  3856. * scale are merged in one "transform" attribute in the SVG node.
  3857. *
  3858. * @private
  3859. * @function Highcharts.SVGElement#afterSetters
  3860. *
  3861. * @return {void}
  3862. */
  3863. afterSetters: function () {
  3864. // Update transform. Do this outside the loop to prevent redundant
  3865. // updating for batch setting of attributes.
  3866. if (this.doTransform) {
  3867. this.updateTransform();
  3868. this.doTransform = false;
  3869. }
  3870. },
  3871. /**
  3872. * Update the shadow elements with new attributes.
  3873. *
  3874. * @private
  3875. * @function Highcharts.SVGElement#updateShadows
  3876. *
  3877. * @param {string} key
  3878. * The attribute name.
  3879. *
  3880. * @param {number} value
  3881. * The value of the attribute.
  3882. *
  3883. * @param {Function} setter
  3884. * The setter function, inherited from the parent wrapper.
  3885. *
  3886. * @return {void}
  3887. */
  3888. updateShadows: function (key, value, setter) {
  3889. var shadows = this.shadows, i = shadows.length;
  3890. while (i--) {
  3891. setter.call(shadows[i], key === 'height' ?
  3892. Math.max(value - (shadows[i].cutHeight || 0), 0) :
  3893. key === 'd' ? this.d : value, key, shadows[i]);
  3894. }
  3895. },
  3896. /**
  3897. * Add a class name to an element.
  3898. *
  3899. * @function Highcharts.SVGElement#addClass
  3900. *
  3901. * @param {string} className
  3902. * The new class name to add.
  3903. *
  3904. * @param {boolean} [replace=false]
  3905. * When true, the existing class name(s) will be overwritten with
  3906. * the new one. When false, the new one is added.
  3907. *
  3908. * @return {Highcharts.SVGElement}
  3909. * Return the SVG element for chainability.
  3910. */
  3911. addClass: function (className, replace) {
  3912. var currentClassName = replace ? '' : (this.attr('class') || '');
  3913. // Trim the string and remove duplicates
  3914. className = (className || '')
  3915. .split(/ /g)
  3916. .reduce(function (newClassName, name) {
  3917. if (currentClassName.indexOf(name) === -1) {
  3918. newClassName.push(name);
  3919. }
  3920. return newClassName;
  3921. }, (currentClassName ?
  3922. [currentClassName] :
  3923. []))
  3924. .join(' ');
  3925. if (className !== currentClassName) {
  3926. this.attr('class', className);
  3927. }
  3928. return this;
  3929. },
  3930. /**
  3931. * Check if an element has the given class name.
  3932. *
  3933. * @function Highcharts.SVGElement#hasClass
  3934. *
  3935. * @param {string} className
  3936. * The class name to check for.
  3937. *
  3938. * @return {boolean}
  3939. * Whether the class name is found.
  3940. */
  3941. hasClass: function (className) {
  3942. return (this.attr('class') || '')
  3943. .split(' ')
  3944. .indexOf(className) !== -1;
  3945. },
  3946. /**
  3947. * Remove a class name from the element.
  3948. *
  3949. * @function Highcharts.SVGElement#removeClass
  3950. *
  3951. * @param {string|RegExp} className
  3952. * The class name to remove.
  3953. *
  3954. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  3955. */
  3956. removeClass: function (className) {
  3957. return this.attr('class', (this.attr('class') || '').replace(isString(className) ?
  3958. new RegExp(" ?" + className + " ?") : // #12064
  3959. className, ''));
  3960. },
  3961. /**
  3962. * If one of the symbol size affecting parameters are changed,
  3963. * check all the others only once for each call to an element's
  3964. * .attr() method
  3965. *
  3966. * @private
  3967. * @function Highcharts.SVGElement#symbolAttr
  3968. *
  3969. * @param {Highcharts.SVGAttributes} hash
  3970. * The attributes to set.
  3971. *
  3972. * @return {void}
  3973. */
  3974. symbolAttr: function (hash) {
  3975. var wrapper = this;
  3976. [
  3977. 'x',
  3978. 'y',
  3979. 'r',
  3980. 'start',
  3981. 'end',
  3982. 'width',
  3983. 'height',
  3984. 'innerR',
  3985. 'anchorX',
  3986. 'anchorY',
  3987. 'clockwise'
  3988. ].forEach(function (key) {
  3989. wrapper[key] = pick(hash[key], wrapper[key]);
  3990. });
  3991. wrapper.attr({
  3992. d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
  3993. });
  3994. },
  3995. /**
  3996. * Apply a clipping rectangle to this element.
  3997. *
  3998. * @function Highcharts.SVGElement#clip
  3999. *
  4000. * @param {Highcharts.ClipRectElement} [clipRect]
  4001. * The clipping rectangle. If skipped, the current clip is removed.
  4002. *
  4003. * @return {Highcharts.SVGElement}
  4004. * Returns the SVG element to allow chaining.
  4005. */
  4006. clip: function (clipRect) {
  4007. return this.attr('clip-path', clipRect ?
  4008. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  4009. 'none');
  4010. },
  4011. /**
  4012. * Calculate the coordinates needed for drawing a rectangle crisply and
  4013. * return the calculated attributes.
  4014. *
  4015. * @function Highcharts.SVGElement#crisp
  4016. *
  4017. * @param {Highcharts.RectangleObject} rect
  4018. * Rectangle to crisp.
  4019. *
  4020. * @param {number} [strokeWidth]
  4021. * The stroke width to consider when computing crisp positioning. It
  4022. * can also be set directly on the rect parameter.
  4023. *
  4024. * @return {Highcharts.RectangleObject}
  4025. * The modified rectangle arguments.
  4026. */
  4027. crisp: function (rect, strokeWidth) {
  4028. var wrapper = this, normalizer;
  4029. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  4030. // Math.round because strokeWidth can sometimes have roundoff errors
  4031. normalizer = Math.round(strokeWidth) % 2 / 2;
  4032. // normalize for crisp edges
  4033. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  4034. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  4035. rect.width = Math.floor((rect.width || wrapper.width || 0) - 2 * normalizer);
  4036. rect.height = Math.floor((rect.height || wrapper.height || 0) - 2 * normalizer);
  4037. if (defined(rect.strokeWidth)) {
  4038. rect.strokeWidth = strokeWidth;
  4039. }
  4040. return rect;
  4041. },
  4042. /**
  4043. * Set styles for the element. In addition to CSS styles supported by
  4044. * native SVG and HTML elements, there are also some custom made for
  4045. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  4046. * elements.
  4047. *
  4048. * @sample highcharts/members/renderer-text-on-chart/
  4049. * Styled text
  4050. *
  4051. * @function Highcharts.SVGElement#css
  4052. *
  4053. * @param {Highcharts.CSSObject} styles
  4054. * The new CSS styles.
  4055. *
  4056. * @return {Highcharts.SVGElement}
  4057. * Return the SVG element for chaining.
  4058. */
  4059. css: function (styles) {
  4060. var oldStyles = this.styles, newStyles = {}, elem = this.element, textWidth, serializedCss = '', hyphenate, hasNew = !oldStyles,
  4061. // These CSS properties are interpreted internally by the SVG
  4062. // renderer, but are not supported by SVG and should not be added to
  4063. // the DOM. In styled mode, no CSS should find its way to the DOM
  4064. // whatsoever (#6173, #6474).
  4065. svgPseudoProps = ['textOutline', 'textOverflow', 'width'];
  4066. // convert legacy
  4067. if (styles && styles.color) {
  4068. styles.fill = styles.color;
  4069. }
  4070. // Filter out existing styles to increase performance (#2640)
  4071. if (oldStyles) {
  4072. objectEach(styles, function (style, n) {
  4073. if (style !== oldStyles[n]) {
  4074. newStyles[n] = style;
  4075. hasNew = true;
  4076. }
  4077. });
  4078. }
  4079. if (hasNew) {
  4080. // Merge the new styles with the old ones
  4081. if (oldStyles) {
  4082. styles = extend(oldStyles, newStyles);
  4083. }
  4084. // Get the text width from style
  4085. if (styles) {
  4086. // Previously set, unset it (#8234)
  4087. if (styles.width === null || styles.width === 'auto') {
  4088. delete this.textWidth;
  4089. // Apply new
  4090. }
  4091. else if (elem.nodeName.toLowerCase() === 'text' &&
  4092. styles.width) {
  4093. textWidth = this.textWidth = pInt(styles.width);
  4094. }
  4095. }
  4096. // store object
  4097. this.styles = styles;
  4098. if (textWidth && (!svg && this.renderer.forExport)) {
  4099. delete styles.width;
  4100. }
  4101. // Serialize and set style attribute
  4102. if (elem.namespaceURI === this.SVG_NS) { // #7633
  4103. hyphenate = function (a, b) {
  4104. return '-' + b.toLowerCase();
  4105. };
  4106. objectEach(styles, function (style, n) {
  4107. if (svgPseudoProps.indexOf(n) === -1) {
  4108. serializedCss +=
  4109. n.replace(/([A-Z])/g, hyphenate) + ':' +
  4110. style + ';';
  4111. }
  4112. });
  4113. if (serializedCss) {
  4114. attr(elem, 'style', serializedCss); // #1881
  4115. }
  4116. }
  4117. else {
  4118. css(elem, styles);
  4119. }
  4120. if (this.added) {
  4121. // Rebuild text after added. Cache mechanisms in the buildText
  4122. // will prevent building if there are no significant changes.
  4123. if (this.element.nodeName === 'text') {
  4124. this.renderer.buildText(this);
  4125. }
  4126. // Apply text outline after added
  4127. if (styles && styles.textOutline) {
  4128. this.applyTextOutline(styles.textOutline);
  4129. }
  4130. }
  4131. }
  4132. return this;
  4133. },
  4134. /**
  4135. * Get the computed style. Only in styled mode.
  4136. *
  4137. * @example
  4138. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  4139. *
  4140. * @function Highcharts.SVGElement#getStyle
  4141. *
  4142. * @param {string} prop
  4143. * The property name to check for.
  4144. *
  4145. * @return {string}
  4146. * The current computed value.
  4147. */
  4148. getStyle: function (prop) {
  4149. return win.getComputedStyle(this.element || this, '')
  4150. .getPropertyValue(prop);
  4151. },
  4152. /**
  4153. * Get the computed stroke width in pixel values. This is used extensively
  4154. * when drawing shapes to ensure the shapes are rendered crisp and
  4155. * positioned correctly relative to each other. Using
  4156. * `shape-rendering: crispEdges` leaves us less control over positioning,
  4157. * for example when we want to stack columns next to each other, or position
  4158. * things pixel-perfectly within the plot box.
  4159. *
  4160. * The common pattern when placing a shape is:
  4161. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  4162. * now receive a stroke width from the style sheet. In classic mode we
  4163. * will add the `stroke-width` attribute.
  4164. * - Read the computed `elem.strokeWidth()`.
  4165. * - Place it based on the stroke width.
  4166. *
  4167. * @function Highcharts.SVGElement#strokeWidth
  4168. *
  4169. * @return {number}
  4170. * The stroke width in pixels. Even if the given stroke widtch (in
  4171. * CSS or by attributes) is based on `em` or other units, the pixel
  4172. * size is returned.
  4173. */
  4174. strokeWidth: function () {
  4175. // In non-styled mode, read the stroke width as set by .attr
  4176. if (!this.renderer.styledMode) {
  4177. return this['stroke-width'] || 0;
  4178. }
  4179. // In styled mode, read computed stroke width
  4180. var val = this.getStyle('stroke-width'), ret = 0, dummy;
  4181. // Read pixel values directly
  4182. if (val.indexOf('px') === val.length - 2) {
  4183. ret = pInt(val);
  4184. // Other values like em, pt etc need to be measured
  4185. }
  4186. else if (val !== '') {
  4187. dummy = doc.createElementNS(SVG_NS, 'rect');
  4188. attr(dummy, {
  4189. width: val,
  4190. 'stroke-width': 0
  4191. });
  4192. this.element.parentNode.appendChild(dummy);
  4193. ret = dummy.getBBox().width;
  4194. dummy.parentNode.removeChild(dummy);
  4195. }
  4196. return ret;
  4197. },
  4198. /**
  4199. * Add an event listener. This is a simple setter that replaces all other
  4200. * events of the same type, opposed to the {@link Highcharts#addEvent}
  4201. * function.
  4202. *
  4203. * @sample highcharts/members/element-on/
  4204. * A clickable rectangle
  4205. *
  4206. * @function Highcharts.SVGElement#on
  4207. *
  4208. * @param {string} eventType
  4209. * The event type. If the type is `click`, Highcharts will internally
  4210. * translate it to a `touchstart` event on touch devices, to prevent
  4211. * the browser from waiting for a click event from firing.
  4212. *
  4213. * @param {Function} handler
  4214. * The handler callback.
  4215. *
  4216. * @return {Highcharts.SVGElement}
  4217. * The SVGElement for chaining.
  4218. */
  4219. on: function (eventType, handler) {
  4220. var svgElement = this, element = svgElement.element;
  4221. // touch
  4222. if (hasTouch && eventType === 'click') {
  4223. element.ontouchstart = function (e) {
  4224. svgElement.touchEventFired = Date.now(); // #2269
  4225. e.preventDefault();
  4226. handler.call(element, e);
  4227. };
  4228. element.onclick = function (e) {
  4229. if (win.navigator.userAgent.indexOf('Android') === -1 ||
  4230. Date.now() - (svgElement.touchEventFired || 0) > 1100) {
  4231. handler.call(element, e);
  4232. }
  4233. };
  4234. }
  4235. else {
  4236. // simplest possible event model for internal use
  4237. element['on' + eventType] = handler;
  4238. }
  4239. return this;
  4240. },
  4241. /**
  4242. * Set the coordinates needed to draw a consistent radial gradient across
  4243. * a shape regardless of positioning inside the chart. Used on pie slices
  4244. * to make all the slices have the same radial reference point.
  4245. *
  4246. * @function Highcharts.SVGElement#setRadialReference
  4247. *
  4248. * @param {Array<number>} coordinates
  4249. * The center reference. The format is `[centerX, centerY, diameter]`
  4250. * in pixels.
  4251. *
  4252. * @return {Highcharts.SVGElement}
  4253. * Returns the SVGElement for chaining.
  4254. */
  4255. setRadialReference: function (coordinates) {
  4256. var existingGradient = this.renderer.gradients[this.element.gradient];
  4257. this.element.radialReference = coordinates;
  4258. // On redrawing objects with an existing gradient, the gradient needs
  4259. // to be repositioned (#3801)
  4260. if (existingGradient && existingGradient.radAttr) {
  4261. existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
  4262. }
  4263. return this;
  4264. },
  4265. /**
  4266. * Move an object and its children by x and y values.
  4267. *
  4268. * @function Highcharts.SVGElement#translate
  4269. *
  4270. * @param {number} x
  4271. * The x value.
  4272. *
  4273. * @param {number} y
  4274. * The y value.
  4275. *
  4276. * @return {Highcharts.SVGElement}
  4277. */
  4278. translate: function (x, y) {
  4279. return this.attr({
  4280. translateX: x,
  4281. translateY: y
  4282. });
  4283. },
  4284. /**
  4285. * Invert a group, rotate and flip. This is used internally on inverted
  4286. * charts, where the points and graphs are drawn as if not inverted, then
  4287. * the series group elements are inverted.
  4288. *
  4289. * @function Highcharts.SVGElement#invert
  4290. *
  4291. * @param {boolean} inverted
  4292. * Whether to invert or not. An inverted shape can be un-inverted by
  4293. * setting it to false.
  4294. *
  4295. * @return {Highcharts.SVGElement}
  4296. * Return the SVGElement for chaining.
  4297. */
  4298. invert: function (inverted) {
  4299. var wrapper = this;
  4300. wrapper.inverted = inverted;
  4301. wrapper.updateTransform();
  4302. return wrapper;
  4303. },
  4304. /**
  4305. * Update the transform attribute based on internal properties. Deals with
  4306. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  4307. * attributes and updates the SVG `transform` attribute.
  4308. *
  4309. * @private
  4310. * @function Highcharts.SVGElement#updateTransform
  4311. *
  4312. * @return {void}
  4313. */
  4314. updateTransform: function () {
  4315. 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;
  4316. // Flipping affects translate as adjustment for flipping around the
  4317. // group's axis
  4318. if (inverted) {
  4319. translateX += wrapper.width;
  4320. translateY += wrapper.height;
  4321. }
  4322. // Apply translate. Nearly all transformed elements have translation,
  4323. // so instead of checking for translate = 0, do it always (#1767,
  4324. // #1846).
  4325. transform = ['translate(' + translateX + ',' + translateY + ')'];
  4326. // apply matrix
  4327. if (defined(matrix)) {
  4328. transform.push('matrix(' + matrix.join(',') + ')');
  4329. }
  4330. // apply rotation
  4331. if (inverted) {
  4332. transform.push('rotate(90) scale(-1,1)');
  4333. }
  4334. else if (rotation) { // text rotation
  4335. transform.push('rotate(' + rotation + ' ' +
  4336. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  4337. ' ' +
  4338. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')');
  4339. }
  4340. // apply scale
  4341. if (defined(scaleX) || defined(scaleY)) {
  4342. transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
  4343. }
  4344. if (transform.length) {
  4345. element.setAttribute('transform', transform.join(' '));
  4346. }
  4347. },
  4348. /**
  4349. * Bring the element to the front. Alternatively, a new zIndex can be set.
  4350. *
  4351. * @sample highcharts/members/element-tofront/
  4352. * Click an element to bring it to front
  4353. *
  4354. * @function Highcharts.SVGElement#toFront
  4355. *
  4356. * @return {Highcharts.SVGElement}
  4357. * Returns the SVGElement for chaining.
  4358. */
  4359. toFront: function () {
  4360. var element = this.element;
  4361. element.parentNode.appendChild(element);
  4362. return this;
  4363. },
  4364. /**
  4365. * Align the element relative to the chart or another box.
  4366. *
  4367. * @function Highcharts.SVGElement#align
  4368. *
  4369. * @param {Highcharts.AlignObject} [alignOptions]
  4370. * The alignment options. The function can be called without this
  4371. * parameter in order to re-align an element after the box has been
  4372. * updated.
  4373. *
  4374. * @param {boolean} [alignByTranslate]
  4375. * Align element by translation.
  4376. *
  4377. * @param {string|Highcharts.BBoxObject} [box]
  4378. * The box to align to, needs a width and height. When the box is a
  4379. * string, it refers to an object in the Renderer. For example, when
  4380. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  4381. * holds `width`, `height`, `x` and `y` properties.
  4382. *
  4383. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  4384. */
  4385. align: function (alignOptions, alignByTranslate, box) {
  4386. var align, vAlign, x, y, attribs = {}, alignTo, renderer = this.renderer, alignedObjects = renderer.alignedObjects, alignFactor, vAlignFactor;
  4387. // First call on instanciate
  4388. if (alignOptions) {
  4389. this.alignOptions = alignOptions;
  4390. this.alignByTranslate = alignByTranslate;
  4391. if (!box || isString(box)) {
  4392. this.alignTo = alignTo = box || 'renderer';
  4393. // prevent duplicates, like legendGroup after resize
  4394. erase(alignedObjects, this);
  4395. alignedObjects.push(this);
  4396. box = null; // reassign it below
  4397. }
  4398. // When called on resize, no arguments are supplied
  4399. }
  4400. else {
  4401. alignOptions = this.alignOptions;
  4402. alignByTranslate = this.alignByTranslate;
  4403. alignTo = this.alignTo;
  4404. }
  4405. box = pick(box, renderer[alignTo], renderer);
  4406. // Assign variables
  4407. align = alignOptions.align;
  4408. vAlign = alignOptions.verticalAlign;
  4409. // default: left align
  4410. x = (box.x || 0) + (alignOptions.x || 0);
  4411. // default: top align
  4412. y = (box.y || 0) + (alignOptions.y || 0);
  4413. // Align
  4414. if (align === 'right') {
  4415. alignFactor = 1;
  4416. }
  4417. else if (align === 'center') {
  4418. alignFactor = 2;
  4419. }
  4420. if (alignFactor) {
  4421. x += (box.width - (alignOptions.width || 0)) /
  4422. alignFactor;
  4423. }
  4424. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  4425. // Vertical align
  4426. if (vAlign === 'bottom') {
  4427. vAlignFactor = 1;
  4428. }
  4429. else if (vAlign === 'middle') {
  4430. vAlignFactor = 2;
  4431. }
  4432. if (vAlignFactor) {
  4433. y += (box.height - (alignOptions.height || 0)) /
  4434. vAlignFactor;
  4435. }
  4436. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  4437. // Animate only if already placed
  4438. this[this.placed ? 'animate' : 'attr'](attribs);
  4439. this.placed = true;
  4440. this.alignAttr = attribs;
  4441. return this;
  4442. },
  4443. /**
  4444. * Get the bounding box (width, height, x and y) for the element. Generally
  4445. * used to get rendered text size. Since this is called a lot in charts,
  4446. * the results are cached based on text properties, in order to save DOM
  4447. * traffic. The returned bounding box includes the rotation, so for example
  4448. * a single text line of rotation 90 will report a greater height, and a
  4449. * width corresponding to the line-height.
  4450. *
  4451. * @sample highcharts/members/renderer-on-chart/
  4452. * Draw a rectangle based on a text's bounding box
  4453. *
  4454. * @function Highcharts.SVGElement#getBBox
  4455. *
  4456. * @param {boolean} [reload]
  4457. * Skip the cache and get the updated DOM bouding box.
  4458. *
  4459. * @param {number} [rot]
  4460. * Override the element's rotation. This is internally used on axis
  4461. * labels with a value of 0 to find out what the bounding box would
  4462. * be have been if it were not rotated.
  4463. *
  4464. * @return {Highcharts.BBoxObject}
  4465. * The bounding box with `x`, `y`, `width` and `height` properties.
  4466. */
  4467. getBBox: function (reload, rot) {
  4468. var wrapper = this, bBox, // = wrapper.bBox,
  4469. 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;
  4470. var rotation = pick(rot, wrapper.rotation, 0);
  4471. fontSize = renderer.styledMode ? (element &&
  4472. SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles && styles.fontSize);
  4473. // Avoid undefined and null (#7316)
  4474. if (defined(textStr)) {
  4475. cacheKey = textStr.toString();
  4476. // Since numbers are monospaced, and numerical labels appear a lot
  4477. // in a chart, we assume that a label of n characters has the same
  4478. // bounding box as others of the same length. Unless there is inner
  4479. // HTML in the label. In that case, leave the numbers as is (#5899).
  4480. if (cacheKey.indexOf('<') === -1) {
  4481. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  4482. }
  4483. // Properties that affect bounding box
  4484. cacheKey += [
  4485. '',
  4486. rotation,
  4487. fontSize,
  4488. wrapper.textWidth,
  4489. styles && styles.textOverflow // #5968
  4490. ].join(',');
  4491. }
  4492. if (cacheKey && !reload) {
  4493. bBox = cache[cacheKey];
  4494. }
  4495. // No cache found
  4496. if (!bBox) {
  4497. // SVG elements
  4498. if (isSVG || renderer.forExport) {
  4499. try { // Fails in Firefox if the container has display: none.
  4500. // When the text shadow shim is used, we need to hide the
  4501. // fake shadows to get the correct bounding box (#3872)
  4502. toggleTextShadowShim = this.fakeTS && function (display) {
  4503. [].forEach.call(element.querySelectorAll('.highcharts-text-outline'), function (tspan) {
  4504. tspan.style.display = display;
  4505. });
  4506. };
  4507. // Workaround for #3842, Firefox reporting wrong bounding
  4508. // box for shadows
  4509. if (toggleTextShadowShim) {
  4510. toggleTextShadowShim('none');
  4511. }
  4512. bBox = element.getBBox ?
  4513. // SVG: use extend because IE9 is not allowed to change
  4514. // width and height in case of rotation (below)
  4515. extend({}, element.getBBox()) : {
  4516. // Legacy IE in export mode
  4517. width: element.offsetWidth,
  4518. height: element.offsetHeight
  4519. };
  4520. // #3842
  4521. if (toggleTextShadowShim) {
  4522. toggleTextShadowShim('');
  4523. }
  4524. }
  4525. catch (e) {
  4526. '';
  4527. }
  4528. // If the bBox is not set, the try-catch block above failed. The
  4529. // other condition is for Opera that returns a width of
  4530. // -Infinity on hidden elements.
  4531. if (!bBox || bBox.width < 0) {
  4532. bBox = { width: 0, height: 0 };
  4533. }
  4534. // VML Renderer or useHTML within SVG
  4535. }
  4536. else {
  4537. bBox = wrapper.htmlGetBBox();
  4538. }
  4539. // True SVG elements as well as HTML elements in modern browsers
  4540. // using the .useHTML option need to compensated for rotation
  4541. if (renderer.isSVG) {
  4542. width = bBox.width;
  4543. height = bBox.height;
  4544. // Workaround for wrong bounding box in IE, Edge and Chrome on
  4545. // Windows. With Highcharts' default font, IE and Edge report
  4546. // a box height of 16.899 and Chrome rounds it to 17. If this
  4547. // stands uncorrected, it results in more padding added below
  4548. // the text than above when adding a label border or background.
  4549. // Also vertical positioning is affected.
  4550. // https://jsfiddle.net/highcharts/em37nvuj/
  4551. // (#1101, #1505, #1669, #2568, #6213).
  4552. if (isSVG) {
  4553. bBox.height = height = ({
  4554. '11px,17': 14,
  4555. '13px,20': 16
  4556. }[styles &&
  4557. styles.fontSize + ',' + Math.round(height)] ||
  4558. height);
  4559. }
  4560. // Adjust for rotated text
  4561. if (rotation) {
  4562. var rad = rotation * deg2rad;
  4563. bBox.width = Math.abs(height * Math.sin(rad)) +
  4564. Math.abs(width * Math.cos(rad));
  4565. bBox.height = Math.abs(height * Math.cos(rad)) +
  4566. Math.abs(width * Math.sin(rad));
  4567. }
  4568. }
  4569. // Cache it. When loading a chart in a hidden iframe in Firefox and
  4570. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  4571. if (cacheKey && bBox.height > 0) {
  4572. // Rotate (#4681)
  4573. while (cacheKeys.length > 250) {
  4574. delete cache[cacheKeys.shift()];
  4575. }
  4576. if (!cache[cacheKey]) {
  4577. cacheKeys.push(cacheKey);
  4578. }
  4579. cache[cacheKey] = bBox;
  4580. }
  4581. }
  4582. return bBox;
  4583. },
  4584. /**
  4585. * Show the element after it has been hidden.
  4586. *
  4587. * @function Highcharts.SVGElement#show
  4588. *
  4589. * @param {boolean} [inherit=false]
  4590. * Set the visibility attribute to `inherit` rather than `visible`.
  4591. * The difference is that an element with `visibility="visible"`
  4592. * will be visible even if the parent is hidden.
  4593. *
  4594. * @return {Highcharts.SVGElement}
  4595. * Returns the SVGElement for chaining.
  4596. */
  4597. show: function (inherit) {
  4598. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  4599. },
  4600. /**
  4601. * Hide the element, similar to setting the `visibility` attribute to
  4602. * `hidden`.
  4603. *
  4604. * @function Highcharts.SVGElement#hide
  4605. *
  4606. * @param {boolean} [hideByTranslation=false]
  4607. * The flag to determine if element should be hidden by moving out
  4608. * of the viewport. Used for example for dataLabels.
  4609. *
  4610. * @return {Highcharts.SVGElement}
  4611. * Returns the SVGElement for chaining.
  4612. */
  4613. hide: function (hideByTranslation) {
  4614. if (hideByTranslation) {
  4615. this.attr({ y: -9999 });
  4616. }
  4617. else {
  4618. this.attr({ visibility: 'hidden' });
  4619. }
  4620. return this;
  4621. },
  4622. /**
  4623. * Fade out an element by animating its opacity down to 0, and hide it on
  4624. * complete. Used internally for the tooltip.
  4625. *
  4626. * @function Highcharts.SVGElement#fadeOut
  4627. *
  4628. * @param {number} [duration=150]
  4629. * The fade duration in milliseconds.
  4630. *
  4631. * @return {void}
  4632. */
  4633. fadeOut: function (duration) {
  4634. var elemWrapper = this;
  4635. elemWrapper.animate({
  4636. opacity: 0
  4637. }, {
  4638. duration: duration || 150,
  4639. complete: function () {
  4640. // #3088, assuming we're only using this for tooltips
  4641. elemWrapper.attr({ y: -9999 });
  4642. }
  4643. });
  4644. },
  4645. /**
  4646. * Add the element to the DOM. All elements must be added this way.
  4647. *
  4648. * @sample highcharts/members/renderer-g
  4649. * Elements added to a group
  4650. *
  4651. * @function Highcharts.SVGElement#add
  4652. *
  4653. * @param {Highcharts.SVGElement} [parent]
  4654. * The parent item to add it to. If undefined, the element is added
  4655. * to the {@link Highcharts.SVGRenderer.box}.
  4656. *
  4657. * @return {Highcharts.SVGElement}
  4658. * Returns the SVGElement for chaining.
  4659. */
  4660. add: function (parent) {
  4661. var renderer = this.renderer, element = this.element, inserted;
  4662. if (parent) {
  4663. this.parentGroup = parent;
  4664. }
  4665. // mark as inverted
  4666. this.parentInverted = parent && parent.inverted;
  4667. // build formatted text
  4668. if (typeof this.textStr !== 'undefined') {
  4669. renderer.buildText(this);
  4670. }
  4671. // Mark as added
  4672. this.added = true;
  4673. // If we're adding to renderer root, or other elements in the group
  4674. // have a z index, we need to handle it
  4675. if (!parent || parent.handleZ || this.zIndex) {
  4676. inserted = this.zIndexSetter();
  4677. }
  4678. // If zIndex is not handled, append at the end
  4679. if (!inserted) {
  4680. (parent ?
  4681. parent.element :
  4682. renderer.box).appendChild(element);
  4683. }
  4684. // fire an event for internal hooks
  4685. if (this.onAdd) {
  4686. this.onAdd();
  4687. }
  4688. return this;
  4689. },
  4690. /**
  4691. * Removes an element from the DOM.
  4692. *
  4693. * @private
  4694. * @function Highcharts.SVGElement#safeRemoveChild
  4695. *
  4696. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  4697. * The DOM node to remove.
  4698. *
  4699. * @return {void}
  4700. */
  4701. safeRemoveChild: function (element) {
  4702. var parentNode = element.parentNode;
  4703. if (parentNode) {
  4704. parentNode.removeChild(element);
  4705. }
  4706. },
  4707. /**
  4708. * Destroy the element and element wrapper and clear up the DOM and event
  4709. * hooks.
  4710. *
  4711. * @function Highcharts.SVGElement#destroy
  4712. *
  4713. * @return {undefined}
  4714. */
  4715. destroy: function () {
  4716. var wrapper = this, element = wrapper.element || {}, renderer = wrapper.renderer, parentToClean = (renderer.isSVG &&
  4717. element.nodeName === 'SPAN' &&
  4718. wrapper.parentGroup), grandParent, ownerSVGElement = element.ownerSVGElement, i, clipPath = wrapper.clipPath;
  4719. // remove events
  4720. element.onclick = element.onmouseout = element.onmouseover =
  4721. element.onmousemove = element.point = null;
  4722. stop(wrapper); // stop running animations
  4723. if (clipPath && ownerSVGElement) {
  4724. // Look for existing references to this clipPath and remove them
  4725. // before destroying the element (#6196).
  4726. // The upper case version is for Edge
  4727. [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
  4728. var clipPathAttr = el.getAttribute('clip-path');
  4729. if (clipPathAttr.indexOf(clipPath.element.id) > -1) {
  4730. el.removeAttribute('clip-path');
  4731. }
  4732. });
  4733. wrapper.clipPath = clipPath.destroy();
  4734. }
  4735. // Destroy stops in case this is a gradient object
  4736. if (wrapper.stops) {
  4737. for (i = 0; i < wrapper.stops.length; i++) {
  4738. wrapper.stops[i] = wrapper.stops[i].destroy();
  4739. }
  4740. wrapper.stops = null;
  4741. }
  4742. // remove element
  4743. wrapper.safeRemoveChild(element);
  4744. if (!renderer.styledMode) {
  4745. wrapper.destroyShadows();
  4746. }
  4747. // In case of useHTML, clean up empty containers emulating SVG groups
  4748. // (#1960, #2393, #2697).
  4749. while (parentToClean &&
  4750. parentToClean.div &&
  4751. parentToClean.div.childNodes.length === 0) {
  4752. grandParent = parentToClean.parentGroup;
  4753. wrapper.safeRemoveChild(parentToClean.div);
  4754. delete parentToClean.div;
  4755. parentToClean = grandParent;
  4756. }
  4757. // remove from alignObjects
  4758. if (wrapper.alignTo) {
  4759. erase(renderer.alignedObjects, wrapper);
  4760. }
  4761. objectEach(wrapper, function (val, key) {
  4762. // Destroy child elements of a group
  4763. if (wrapper[key] &&
  4764. wrapper[key].parentGroup === wrapper &&
  4765. wrapper[key].destroy) {
  4766. wrapper[key].destroy();
  4767. }
  4768. // Delete all properties
  4769. delete wrapper[key];
  4770. });
  4771. return;
  4772. },
  4773. /**
  4774. * Add a shadow to the element. Must be called after the element is added to
  4775. * the DOM. In styled mode, this method is not used, instead use `defs` and
  4776. * filters.
  4777. *
  4778. * @example
  4779. * renderer.rect(10, 100, 100, 100)
  4780. * .attr({ fill: 'red' })
  4781. * .shadow(true);
  4782. *
  4783. * @function Highcharts.SVGElement#shadow
  4784. *
  4785. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions]
  4786. * The shadow options. If `true`, the default options are applied. If
  4787. * `false`, the current shadow will be removed.
  4788. *
  4789. * @param {Highcharts.SVGElement} [group]
  4790. * The SVG group element where the shadows will be applied. The
  4791. * default is to add it to the same parent as the current element.
  4792. * Internally, this is ised for pie slices, where all the shadows are
  4793. * added to an element behind all the slices.
  4794. *
  4795. * @param {boolean} [cutOff]
  4796. * Used internally for column shadows.
  4797. *
  4798. * @return {Highcharts.SVGElement}
  4799. * Returns the SVGElement for chaining.
  4800. */
  4801. shadow: function (shadowOptions, group, cutOff) {
  4802. var shadows = [], i, shadow, element = this.element, strokeWidth, shadowWidth, shadowElementOpacity,
  4803. // compensate for inverted plot area
  4804. transform;
  4805. if (!shadowOptions) {
  4806. this.destroyShadows();
  4807. }
  4808. else if (!this.shadows) {
  4809. shadowWidth = pick(shadowOptions.width, 3);
  4810. shadowElementOpacity = (shadowOptions.opacity || 0.15) /
  4811. shadowWidth;
  4812. transform = this.parentInverted ?
  4813. '(-1,-1)' :
  4814. '(' + pick(shadowOptions.offsetX, 1) + ', ' +
  4815. pick(shadowOptions.offsetY, 1) + ')';
  4816. for (i = 1; i <= shadowWidth; i++) {
  4817. shadow = element.cloneNode(0);
  4818. strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
  4819. attr(shadow, {
  4820. stroke: (shadowOptions.color ||
  4821. '#000000'),
  4822. 'stroke-opacity': shadowElementOpacity * i,
  4823. 'stroke-width': strokeWidth,
  4824. transform: 'translate' + transform,
  4825. fill: 'none'
  4826. });
  4827. shadow.setAttribute('class', (shadow.getAttribute('class') || '') + ' highcharts-shadow');
  4828. if (cutOff) {
  4829. attr(shadow, 'height', Math.max(attr(shadow, 'height') - strokeWidth, 0));
  4830. shadow.cutHeight = strokeWidth;
  4831. }
  4832. if (group) {
  4833. group.element.appendChild(shadow);
  4834. }
  4835. else if (element.parentNode) {
  4836. element.parentNode.insertBefore(shadow, element);
  4837. }
  4838. shadows.push(shadow);
  4839. }
  4840. this.shadows = shadows;
  4841. }
  4842. return this;
  4843. },
  4844. /**
  4845. * Destroy shadows on the element.
  4846. *
  4847. * @private
  4848. * @function Highcharts.SVGElement#destroyShadows
  4849. *
  4850. * @return {void}
  4851. */
  4852. destroyShadows: function () {
  4853. (this.shadows || []).forEach(function (shadow) {
  4854. this.safeRemoveChild(shadow);
  4855. }, this);
  4856. this.shadows = void 0;
  4857. },
  4858. /**
  4859. * @private
  4860. * @function Highcharts.SVGElement#xGetter
  4861. *
  4862. * @param {string} key - key
  4863. *
  4864. * @return {number|string|null}
  4865. */
  4866. xGetter: function (key) {
  4867. if (this.element.nodeName === 'circle') {
  4868. if (key === 'x') {
  4869. key = 'cx';
  4870. }
  4871. else if (key === 'y') {
  4872. key = 'cy';
  4873. }
  4874. }
  4875. return this._defaultGetter(key);
  4876. },
  4877. /**
  4878. * Get the current value of an attribute or pseudo attribute,
  4879. * used mainly for animation. Called internally from
  4880. * the {@link Highcharts.SVGRenderer#attr} function.
  4881. *
  4882. * @private
  4883. * @function Highcharts.SVGElement#_defaultGetter
  4884. *
  4885. * @param {string} key
  4886. * Property key.
  4887. *
  4888. * @return {number|string}
  4889. * Property value.
  4890. */
  4891. _defaultGetter: function (key) {
  4892. var ret = pick(this[key + 'Value'], // align getter
  4893. this[key], this.element ? this.element.getAttribute(key) : null, 0);
  4894. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  4895. ret = parseFloat(ret);
  4896. }
  4897. return ret;
  4898. },
  4899. /**
  4900. * @private
  4901. * @function Highcharts.SVGElement#dSettter
  4902. *
  4903. * @param {number|string|Highcharts.SVGPathArray} value
  4904. *
  4905. * @param {string} key
  4906. *
  4907. * @param {Highcharts.SVGDOMElement} element
  4908. *
  4909. * @return {void}
  4910. */
  4911. dSetter: function (value, key, element) {
  4912. if (value && value.join) { // join path
  4913. value = value.join(' ');
  4914. }
  4915. if (/(NaN| {2}|^$)/.test(value)) {
  4916. value = 'M 0 0';
  4917. }
  4918. // Check for cache before resetting. Resetting causes disturbance in the
  4919. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  4920. // possible performance gain.
  4921. if (this[key] !== value) {
  4922. element.setAttribute(key, value);
  4923. this[key] = value;
  4924. }
  4925. },
  4926. /**
  4927. * @private
  4928. * @function Highcharts.SVGElement#dashstyleSetter
  4929. *
  4930. * @param {string} value
  4931. *
  4932. * @return {void}
  4933. */
  4934. dashstyleSetter: function (value) {
  4935. var i, strokeWidth = this['stroke-width'];
  4936. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  4937. // strokeWidth function, we should be able to use that instead.
  4938. if (strokeWidth === 'inherit') {
  4939. strokeWidth = 1;
  4940. }
  4941. value = value && value.toLowerCase();
  4942. if (value) {
  4943. value = value
  4944. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  4945. .replace('shortdashdot', '3,1,1,1')
  4946. .replace('shortdot', '1,1,')
  4947. .replace('shortdash', '3,1,')
  4948. .replace('longdash', '8,3,')
  4949. .replace(/dot/g, '1,3,')
  4950. .replace('dash', '4,3,')
  4951. .replace(/,$/, '')
  4952. .split(','); // ending comma
  4953. i = value.length;
  4954. while (i--) {
  4955. value[i] = pInt(value[i]) * strokeWidth;
  4956. }
  4957. value = value.join(',')
  4958. .replace(/NaN/g, 'none'); // #3226
  4959. this.element.setAttribute('stroke-dasharray', value);
  4960. }
  4961. },
  4962. /**
  4963. * @private
  4964. * @function Highcharts.SVGElement#alignSetter
  4965. *
  4966. * @param {"start"|"middle"|"end"} value
  4967. *
  4968. * @return {void}
  4969. */
  4970. alignSetter: function (value) {
  4971. var convert = {
  4972. left: 'start',
  4973. center: 'middle',
  4974. right: 'end'
  4975. };
  4976. if (convert[value]) {
  4977. this.alignValue = value;
  4978. this.element.setAttribute('text-anchor', convert[value]);
  4979. }
  4980. },
  4981. /**
  4982. * @private
  4983. * @function Highcharts.SVGElement#opacitySetter
  4984. *
  4985. * @param {string} value
  4986. *
  4987. * @param {string} key
  4988. *
  4989. * @param {Highcharts.SVGDOMElement} element
  4990. *
  4991. * @return {void}
  4992. */
  4993. opacitySetter: function (value, key, element) {
  4994. this[key] = value;
  4995. element.setAttribute(key, value);
  4996. },
  4997. /**
  4998. * @private
  4999. * @function Highcharts.SVGElement#titleSetter
  5000. *
  5001. * @param {string} value
  5002. *
  5003. * @return {void}
  5004. */
  5005. titleSetter: function (value) {
  5006. var titleNode = this.element.getElementsByTagName('title')[0];
  5007. if (!titleNode) {
  5008. titleNode = doc.createElementNS(this.SVG_NS, 'title');
  5009. this.element.appendChild(titleNode);
  5010. }
  5011. // Remove text content if it exists
  5012. if (titleNode.firstChild) {
  5013. titleNode.removeChild(titleNode.firstChild);
  5014. }
  5015. titleNode.appendChild(doc.createTextNode(
  5016. // #3276, #3895
  5017. String(pick(value, ''))
  5018. .replace(/<[^>]*>/g, '')
  5019. .replace(/&lt;/g, '<')
  5020. .replace(/&gt;/g, '>')));
  5021. },
  5022. /**
  5023. * @private
  5024. * @function Highcharts.SVGElement#textSetter
  5025. *
  5026. * @param {string} value
  5027. *
  5028. * @return {void}
  5029. */
  5030. textSetter: function (value) {
  5031. if (value !== this.textStr) {
  5032. // Delete size caches when the text changes
  5033. delete this.bBox;
  5034. delete this.textPxLength;
  5035. this.textStr = value;
  5036. if (this.added) {
  5037. this.renderer.buildText(this);
  5038. }
  5039. }
  5040. },
  5041. /**
  5042. * @private
  5043. * @function Highcharts.SVGElement#setTextPath
  5044. * @param {Highcharts.SVGElement} path - path to follow
  5045. * @param {Highcharts.DataLabelsTextPathOptionsObject} textPathOptions - options
  5046. * @return {Highcharts.SVGElement}
  5047. * Returns the SVGElement for chaining.
  5048. */
  5049. setTextPath: function (path, textPathOptions) {
  5050. var elem = this.element, attribsMap = {
  5051. textAnchor: 'text-anchor'
  5052. }, attrs, adder = false, textPathElement, textPathId, textPathWrapper = this.textPathWrapper, tspans, firstTime = !textPathWrapper;
  5053. // Defaults
  5054. textPathOptions = merge(true, {
  5055. enabled: true,
  5056. attributes: {
  5057. dy: -5,
  5058. startOffset: '50%',
  5059. textAnchor: 'middle'
  5060. }
  5061. }, textPathOptions);
  5062. attrs = textPathOptions.attributes;
  5063. if (path && textPathOptions && textPathOptions.enabled) {
  5064. // In case of fixed width for a text, string is rebuilt
  5065. // (e.g. ellipsis is applied), so we need to rebuild textPath too
  5066. if (textPathWrapper &&
  5067. textPathWrapper.element.parentNode === null) {
  5068. // When buildText functionality was triggered again
  5069. // and deletes textPathWrapper parentNode
  5070. firstTime = true;
  5071. textPathWrapper = textPathWrapper.destroy();
  5072. }
  5073. else if (textPathWrapper) {
  5074. // Case after drillup when spans were added into
  5075. // the DOM outside the textPathWrapper parentGroup
  5076. this.removeTextOutline.call(textPathWrapper.parentGroup, [].slice.call(elem.getElementsByTagName('tspan')));
  5077. }
  5078. // label() has padding, text() doesn't
  5079. if (this.options && this.options.padding) {
  5080. attrs.dx = -this.options.padding;
  5081. }
  5082. if (!textPathWrapper) {
  5083. // Create <textPath>, defer the DOM adder
  5084. this.textPathWrapper = textPathWrapper =
  5085. this.renderer.createElement('textPath');
  5086. adder = true;
  5087. }
  5088. textPathElement = textPathWrapper.element;
  5089. // Set ID for the path
  5090. textPathId = path.element.getAttribute('id');
  5091. if (!textPathId) {
  5092. path.element.setAttribute('id', textPathId = H.uniqueKey());
  5093. }
  5094. // Change DOM structure, by placing <textPath> tag in <text>
  5095. if (firstTime) {
  5096. tspans = elem.getElementsByTagName('tspan');
  5097. // Now move all <tspan>'s to the <textPath> node
  5098. while (tspans.length) {
  5099. // Remove "y" from tspans, as Firefox translates them
  5100. tspans[0].setAttribute('y', 0);
  5101. // Remove "x" from tspans
  5102. if (isNumber(attrs.dx)) {
  5103. tspans[0].setAttribute('x', -attrs.dx);
  5104. }
  5105. textPathElement.appendChild(tspans[0]);
  5106. }
  5107. }
  5108. // Add <textPath> to the DOM
  5109. if (adder) {
  5110. textPathWrapper.add({
  5111. // label() is placed in a group, text() is standalone
  5112. element: this.text ? this.text.element : elem
  5113. });
  5114. }
  5115. // Set basic options:
  5116. // Use `setAttributeNS` because Safari needs this..
  5117. textPathElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.renderer.url + '#' + textPathId);
  5118. // Presentation attributes:
  5119. // dx/dy options must by set on <text> (parent),
  5120. // the rest should be set on <textPath>
  5121. if (defined(attrs.dy)) {
  5122. textPathElement.parentNode
  5123. .setAttribute('dy', attrs.dy);
  5124. delete attrs.dy;
  5125. }
  5126. if (defined(attrs.dx)) {
  5127. textPathElement.parentNode
  5128. .setAttribute('dx', attrs.dx);
  5129. delete attrs.dx;
  5130. }
  5131. // Additional attributes
  5132. objectEach(attrs, function (val, key) {
  5133. textPathElement.setAttribute(attribsMap[key] || key, val);
  5134. });
  5135. // Remove translation, text that follows path does not need that
  5136. elem.removeAttribute('transform');
  5137. // Remove shadows and text outlines
  5138. this.removeTextOutline.call(textPathWrapper, [].slice.call(elem.getElementsByTagName('tspan')));
  5139. // Remove background and border for label(), see #10545
  5140. // Alternatively, we can disable setting background rects in
  5141. // series.drawDataLabels()
  5142. if (this.text && !this.renderer.styledMode) {
  5143. this.attr({
  5144. fill: 'none',
  5145. 'stroke-width': 0
  5146. });
  5147. }
  5148. // Disable some functions
  5149. this.updateTransform = noop;
  5150. this.applyTextOutline = noop;
  5151. }
  5152. else if (textPathWrapper) {
  5153. // Reset to prototype
  5154. delete this.updateTransform;
  5155. delete this.applyTextOutline;
  5156. // Restore DOM structure:
  5157. this.destroyTextPath(elem, path);
  5158. // Bring attributes back
  5159. this.updateTransform();
  5160. // Set textOutline back for text()
  5161. if (this.options.rotation) {
  5162. this.applyTextOutline(this.options.style.textOutline);
  5163. }
  5164. }
  5165. return this;
  5166. },
  5167. destroyTextPath: function (elem, path) {
  5168. var tspans, textElement = elem.getElementsByTagName('text')[0];
  5169. if (textElement) {
  5170. // Remove textPath attributes
  5171. textElement.removeAttribute('dx');
  5172. textElement.removeAttribute('dy');
  5173. // Remove ID's:
  5174. path.element.setAttribute('id', '');
  5175. // Check if textElement includes textPath,
  5176. if (textElement.getElementsByTagName('textPath').length) {
  5177. // Move nodes to <text>
  5178. tspans = this.textPathWrapper.element.childNodes;
  5179. // Now move all <tspan>'s to the <textPath> node
  5180. while (tspans.length) {
  5181. textElement.appendChild(tspans[0]);
  5182. }
  5183. // Remove <textPath> from the DOM
  5184. textElement.removeChild(this.textPathWrapper.element);
  5185. }
  5186. }
  5187. else if (elem.getAttribute('dx') || elem.getAttribute('dy')) {
  5188. // Remove textPath attributes from elem
  5189. // to get correct text-outline position
  5190. elem.removeAttribute('dx');
  5191. elem.removeAttribute('dy');
  5192. }
  5193. // Set textPathWrapper to undefined and destroy it
  5194. this.textPathWrapper = this.textPathWrapper.destroy();
  5195. },
  5196. /**
  5197. * @private
  5198. * @function Highcharts.SVGElement#fillSetter
  5199. *
  5200. * @param {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} value
  5201. *
  5202. * @param {string} key
  5203. *
  5204. * @param {Highcharts.SVGDOMElement} element
  5205. *
  5206. * @return {void}
  5207. */
  5208. fillSetter: function (value, key, element) {
  5209. if (typeof value === 'string') {
  5210. element.setAttribute(key, value);
  5211. }
  5212. else if (value) {
  5213. this.complexColor(value, key, element);
  5214. }
  5215. },
  5216. /**
  5217. * @private
  5218. * @function Highcharts.SVGElement#visibilitySetter
  5219. *
  5220. * @param {string} value
  5221. *
  5222. * @param {string} key
  5223. *
  5224. * @param {Highcharts.SVGDOMElement} element
  5225. *
  5226. * @return {void}
  5227. */
  5228. visibilitySetter: function (value, key, element) {
  5229. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  5230. // attribute instead (#2881, #3909)
  5231. if (value === 'inherit') {
  5232. element.removeAttribute(key);
  5233. }
  5234. else if (this[key] !== value) { // #6747
  5235. element.setAttribute(key, value);
  5236. }
  5237. this[key] = value;
  5238. },
  5239. /**
  5240. * @private
  5241. * @function Highcharts.SVGElement#zIndexSetter
  5242. * @param {string} [value] - value
  5243. * @param {string} [key] - key
  5244. * @return {boolean}
  5245. */
  5246. zIndexSetter: function (value, key) {
  5247. 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;
  5248. if (defined(value)) {
  5249. // So we can read it for other elements in the group
  5250. element.setAttribute('data-z-index', value);
  5251. value = +value;
  5252. if (this[key] === value) {
  5253. // Only update when needed (#3865)
  5254. run = false;
  5255. }
  5256. }
  5257. else if (defined(this[key])) {
  5258. element.removeAttribute('data-z-index');
  5259. }
  5260. this[key] = value;
  5261. // Insert according to this and other elements' zIndex. Before .add() is
  5262. // called, nothing is done. Then on add, or by later calls to
  5263. // zIndexSetter, the node is placed on the right place in the DOM.
  5264. if (run) {
  5265. value = this.zIndex;
  5266. if (value && parentGroup) {
  5267. parentGroup.handleZ = true;
  5268. }
  5269. childNodes = parentNode.childNodes;
  5270. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  5271. otherElement = childNodes[i];
  5272. otherZIndex = otherElement.getAttribute('data-z-index');
  5273. undefinedOtherZIndex = !defined(otherZIndex);
  5274. if (otherElement !== element) {
  5275. if (
  5276. // Negative zIndex versus no zIndex:
  5277. // On all levels except the highest. If the parent is
  5278. // <svg>, then we don't want to put items before <desc>
  5279. // or <defs>
  5280. value < 0 &&
  5281. undefinedOtherZIndex &&
  5282. !svgParent &&
  5283. !i) {
  5284. parentNode.insertBefore(element, childNodes[i]);
  5285. inserted = true;
  5286. }
  5287. else if (
  5288. // Insert after the first element with a lower zIndex
  5289. pInt(otherZIndex) <= value ||
  5290. // If negative zIndex, add this before first undefined
  5291. // zIndex element
  5292. (undefinedOtherZIndex &&
  5293. (!defined(value) || value >= 0))) {
  5294. parentNode.insertBefore(element, childNodes[i + 1] || null // null for oldIE export
  5295. );
  5296. inserted = true;
  5297. }
  5298. }
  5299. }
  5300. if (!inserted) {
  5301. parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0] || null // null for oldIE
  5302. );
  5303. inserted = true;
  5304. }
  5305. }
  5306. return inserted;
  5307. },
  5308. /**
  5309. * @private
  5310. * @function Highcharts.SVGElement#_defaultSetter
  5311. *
  5312. * @param {string} value
  5313. *
  5314. * @param {string} key
  5315. *
  5316. * @param {Highcharts.SVGDOMElement} element
  5317. *
  5318. * @return {void}
  5319. */
  5320. _defaultSetter: function (value, key, element) {
  5321. element.setAttribute(key, value);
  5322. }
  5323. });
  5324. // Some shared setters and getters
  5325. SVGElement.prototype.yGetter =
  5326. SVGElement.prototype.xGetter;
  5327. SVGElement.prototype.translateXSetter =
  5328. SVGElement.prototype.translateYSetter =
  5329. SVGElement.prototype.rotationSetter =
  5330. SVGElement.prototype.verticalAlignSetter =
  5331. SVGElement.prototype.rotationOriginXSetter =
  5332. SVGElement.prototype.rotationOriginYSetter =
  5333. SVGElement.prototype.scaleXSetter =
  5334. SVGElement.prototype.scaleYSetter =
  5335. SVGElement.prototype.matrixSetter = function (value, key) {
  5336. this[key] = value;
  5337. this.doTransform = true;
  5338. };
  5339. // WebKit and Batik have problems with a stroke-width of zero, so in this case
  5340. // we remove the stroke attribute altogether. #1270, #1369, #3065, #3072.
  5341. SVGElement.prototype['stroke-widthSetter'] =
  5342. /**
  5343. * @private
  5344. * @function Highcharts.SVGElement#strokeSetter
  5345. *
  5346. * @param {number|string} value
  5347. *
  5348. * @param {string} key
  5349. *
  5350. * @param {Highcharts.SVGDOMElement} element
  5351. *
  5352. * @return {void}
  5353. */
  5354. SVGElement.prototype.strokeSetter = function (value, key, element) {
  5355. this[key] = value;
  5356. // Only apply the stroke attribute if the stroke width is defined and larger
  5357. // than 0
  5358. if (this.stroke && this['stroke-width']) {
  5359. // Use prototype as instance may be overridden
  5360. SVGElement.prototype.fillSetter.call(this, this.stroke, 'stroke', element);
  5361. element.setAttribute('stroke-width', this['stroke-width']);
  5362. this.hasStroke = true;
  5363. }
  5364. else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
  5365. element.removeAttribute('stroke');
  5366. this.hasStroke = false;
  5367. }
  5368. else if (this.renderer.styledMode && this['stroke-width']) {
  5369. element.setAttribute('stroke-width', this['stroke-width']);
  5370. this.hasStroke = true;
  5371. }
  5372. };
  5373. /**
  5374. * Allows direct access to the Highcharts rendering layer in order to draw
  5375. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  5376. * or independent from any chart. The SVGRenderer represents a wrapper object
  5377. * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
  5378. * module, it also brings vector graphics to IE <= 8.
  5379. *
  5380. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  5381. * The renderer can also be used completely decoupled from a chart.
  5382. *
  5383. * @sample highcharts/members/renderer-on-chart
  5384. * Annotating a chart programmatically.
  5385. * @sample highcharts/members/renderer-basic
  5386. * Independent SVG drawing.
  5387. *
  5388. * @example
  5389. * // Use directly without a chart object.
  5390. * var renderer = new Highcharts.Renderer(parentNode, 600, 400);
  5391. *
  5392. * @class
  5393. * @name Highcharts.SVGRenderer
  5394. *
  5395. * @param {Highcharts.HTMLDOMElement} container
  5396. * Where to put the SVG in the web page.
  5397. *
  5398. * @param {number} width
  5399. * The width of the SVG.
  5400. *
  5401. * @param {number} height
  5402. * The height of the SVG.
  5403. *
  5404. * @param {Highcharts.CSSObject} [style]
  5405. * The box style, if not in styleMode
  5406. *
  5407. * @param {boolean} [forExport=false]
  5408. * Whether the rendered content is intended for export.
  5409. *
  5410. * @param {boolean} [allowHTML=true]
  5411. * Whether the renderer is allowed to include HTML text, which will be
  5412. * projected on top of the SVG.
  5413. *
  5414. * @param {boolean} [styledMode=false]
  5415. * Whether the renderer belongs to a chart that is in styled mode.
  5416. * If it does, it will avoid setting presentational attributes in
  5417. * some cases, but not when set explicitly through `.attr` and `.css`
  5418. * etc.
  5419. */
  5420. SVGRenderer = H.SVGRenderer = function () {
  5421. this.init.apply(this, arguments);
  5422. };
  5423. extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ {
  5424. /**
  5425. * A pointer to the renderer's associated Element class. The VMLRenderer
  5426. * will have a pointer to VMLElement here.
  5427. *
  5428. * @name Highcharts.SVGRenderer#Element
  5429. * @type {Highcharts.SVGElement}
  5430. */
  5431. Element: SVGElement,
  5432. SVG_NS: SVG_NS,
  5433. /**
  5434. * Initialize the SVGRenderer. Overridable initializer function that takes
  5435. * the same parameters as the constructor.
  5436. *
  5437. * @function Highcharts.SVGRenderer#init
  5438. *
  5439. * @param {Highcharts.HTMLDOMElement} container
  5440. * Where to put the SVG in the web page.
  5441. *
  5442. * @param {number} width
  5443. * The width of the SVG.
  5444. *
  5445. * @param {number} height
  5446. * The height of the SVG.
  5447. *
  5448. * @param {Highcharts.CSSObject} [style]
  5449. * The box style, if not in styleMode
  5450. *
  5451. * @param {boolean} [forExport=false]
  5452. * Whether the rendered content is intended for export.
  5453. *
  5454. * @param {boolean} [allowHTML=true]
  5455. * Whether the renderer is allowed to include HTML text, which will
  5456. * be projected on top of the SVG.
  5457. *
  5458. * @param {boolean} [styledMode=false]
  5459. * Whether the renderer belongs to a chart that is in styled mode.
  5460. * If it does, it will avoid setting presentational attributes in
  5461. * some cases, but not when set explicitly through `.attr` and `.css`
  5462. * etc.
  5463. *
  5464. * @return {void}
  5465. */
  5466. init: function (container, width, height, style, forExport, allowHTML, styledMode) {
  5467. var renderer = this, boxWrapper, element, desc;
  5468. boxWrapper = renderer.createElement('svg')
  5469. .attr({
  5470. version: '1.1',
  5471. 'class': 'highcharts-root'
  5472. });
  5473. if (!styledMode) {
  5474. boxWrapper.css(this.getStyle(style));
  5475. }
  5476. element = boxWrapper.element;
  5477. container.appendChild(element);
  5478. // Always use ltr on the container, otherwise text-anchor will be
  5479. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  5480. attr(container, 'dir', 'ltr');
  5481. // For browsers other than IE, add the namespace attribute (#1978)
  5482. if (container.innerHTML.indexOf('xmlns') === -1) {
  5483. attr(element, 'xmlns', this.SVG_NS);
  5484. }
  5485. // object properties
  5486. renderer.isSVG = true;
  5487. /**
  5488. * The root `svg` node of the renderer.
  5489. *
  5490. * @name Highcharts.SVGRenderer#box
  5491. * @type {Highcharts.SVGDOMElement}
  5492. */
  5493. this.box = element;
  5494. /**
  5495. * The wrapper for the root `svg` node of the renderer.
  5496. *
  5497. * @name Highcharts.SVGRenderer#boxWrapper
  5498. * @type {Highcharts.SVGElement}
  5499. */
  5500. this.boxWrapper = boxWrapper;
  5501. renderer.alignedObjects = [];
  5502. /**
  5503. * Page url used for internal references.
  5504. *
  5505. * @private
  5506. * @name Highcharts.SVGRenderer#url
  5507. * @type {string}
  5508. */
  5509. // #24, #672, #1070
  5510. this.url = ((isFirefox || isWebKit) &&
  5511. doc.getElementsByTagName('base').length) ?
  5512. win.location.href
  5513. .split('#')[0] // remove the hash
  5514. .replace(/<[^>]*>/g, '') // wing cut HTML
  5515. // escape parantheses and quotes
  5516. .replace(/([\('\)])/g, '\\$1')
  5517. // replace spaces (needed for Safari only)
  5518. .replace(/ /g, '%20') :
  5519. '';
  5520. // Add description
  5521. desc = this.createElement('desc').add();
  5522. desc.element.appendChild(doc.createTextNode('Created with Highcharts 8.0.0'));
  5523. /**
  5524. * A pointer to the `defs` node of the root SVG.
  5525. *
  5526. * @name Highcharts.SVGRenderer#defs
  5527. * @type {Highcharts.SVGElement}
  5528. */
  5529. renderer.defs = this.createElement('defs').add();
  5530. renderer.allowHTML = allowHTML;
  5531. renderer.forExport = forExport;
  5532. renderer.styledMode = styledMode;
  5533. renderer.gradients = {}; // Object where gradient SvgElements are stored
  5534. renderer.cache = {}; // Cache for numerical bounding boxes
  5535. renderer.cacheKeys = [];
  5536. renderer.imgCount = 0;
  5537. renderer.setSize(width, height, false);
  5538. // Issue 110 workaround:
  5539. // In Firefox, if a div is positioned by percentage, its pixel position
  5540. // may land between pixels. The container itself doesn't display this,
  5541. // but an SVG element inside this container will be drawn at subpixel
  5542. // precision. In order to draw sharp lines, this must be compensated
  5543. // for. This doesn't seem to work inside iframes though (like in
  5544. // jsFiddle).
  5545. var subPixelFix, rect;
  5546. if (isFirefox && container.getBoundingClientRect) {
  5547. subPixelFix = function () {
  5548. css(container, { left: 0, top: 0 });
  5549. rect = container.getBoundingClientRect();
  5550. css(container, {
  5551. left: (Math.ceil(rect.left) - rect.left) + 'px',
  5552. top: (Math.ceil(rect.top) - rect.top) + 'px'
  5553. });
  5554. };
  5555. // run the fix now
  5556. subPixelFix();
  5557. // run it on resize
  5558. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  5559. }
  5560. },
  5561. /**
  5562. * General method for adding a definition to the SVG `defs` tag. Can be used
  5563. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  5564. * general definitions to the SVG's defs tag. Definitions can be referenced
  5565. * from the CSS by its `id`. Read more in
  5566. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  5567. * Styled mode only.
  5568. *
  5569. * @function Highcharts.SVGRenderer#definition
  5570. *
  5571. * @param {Highcharts.SVGDefinitionObject} def
  5572. * A serialized form of an SVG definition, including children.
  5573. *
  5574. * @return {Highcharts.SVGElement}
  5575. * The inserted node.
  5576. */
  5577. definition: function (def) {
  5578. var ren = this;
  5579. /**
  5580. * @private
  5581. * @param {Highcharts.SVGDefinitionObject} config - SVG definition
  5582. * @param {Highcharts.SVGElement} [parent] - parent node
  5583. */
  5584. function recurse(config, parent) {
  5585. var ret;
  5586. splat(config).forEach(function (item) {
  5587. var node = ren.createElement(item.tagName), attr = {};
  5588. // Set attributes
  5589. objectEach(item, function (val, key) {
  5590. if (key !== 'tagName' &&
  5591. key !== 'children' &&
  5592. key !== 'textContent') {
  5593. attr[key] = val;
  5594. }
  5595. });
  5596. node.attr(attr);
  5597. // Add to the tree
  5598. node.add(parent || ren.defs);
  5599. // Add text content
  5600. if (item.textContent) {
  5601. node.element.appendChild(doc.createTextNode(item.textContent));
  5602. }
  5603. // Recurse
  5604. recurse(item.children || [], node);
  5605. ret = node;
  5606. });
  5607. // Return last node added (on top level it's the only one)
  5608. return ret;
  5609. }
  5610. return recurse(def);
  5611. },
  5612. /**
  5613. * Get the global style setting for the renderer.
  5614. *
  5615. * @private
  5616. * @function Highcharts.SVGRenderer#getStyle
  5617. *
  5618. * @param {Highcharts.CSSObject} style
  5619. * Style settings.
  5620. *
  5621. * @return {Highcharts.CSSObject}
  5622. * The style settings mixed with defaults.
  5623. */
  5624. getStyle: function (style) {
  5625. this.style = extend({
  5626. fontFamily: '"Lucida Grande", "Lucida Sans Unicode", ' +
  5627. 'Arial, Helvetica, sans-serif',
  5628. fontSize: '12px'
  5629. }, style);
  5630. return this.style;
  5631. },
  5632. /**
  5633. * Apply the global style on the renderer, mixed with the default styles.
  5634. *
  5635. * @function Highcharts.SVGRenderer#setStyle
  5636. *
  5637. * @param {Highcharts.CSSObject} style
  5638. * CSS to apply.
  5639. */
  5640. setStyle: function (style) {
  5641. this.boxWrapper.css(this.getStyle(style));
  5642. },
  5643. /**
  5644. * Detect whether the renderer is hidden. This happens when one of the
  5645. * parent elements has `display: none`. Used internally to detect when we
  5646. * needto render preliminarily in another div to get the text bounding boxes
  5647. * right.
  5648. *
  5649. * @function Highcharts.SVGRenderer#isHidden
  5650. *
  5651. * @return {boolean}
  5652. * True if it is hidden.
  5653. */
  5654. isHidden: function () {
  5655. return !this.boxWrapper.getBBox().width;
  5656. },
  5657. /**
  5658. * Destroys the renderer and its allocated members.
  5659. *
  5660. * @function Highcharts.SVGRenderer#destroy
  5661. *
  5662. * @return {null}
  5663. */
  5664. destroy: function () {
  5665. var renderer = this, rendererDefs = renderer.defs;
  5666. renderer.box = null;
  5667. renderer.boxWrapper = renderer.boxWrapper.destroy();
  5668. // Call destroy on all gradient elements
  5669. destroyObjectProperties(renderer.gradients || {});
  5670. renderer.gradients = null;
  5671. // Defs are null in VMLRenderer
  5672. // Otherwise, destroy them here.
  5673. if (rendererDefs) {
  5674. renderer.defs = rendererDefs.destroy();
  5675. }
  5676. // Remove sub pixel fix handler (#982)
  5677. if (renderer.unSubPixelFix) {
  5678. renderer.unSubPixelFix();
  5679. }
  5680. renderer.alignedObjects = null;
  5681. return null;
  5682. },
  5683. /**
  5684. * Create a wrapper for an SVG element. Serves as a factory for
  5685. * {@link SVGElement}, but this function is itself mostly called from
  5686. * primitive factories like {@link SVGRenderer#path}, {@link
  5687. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  5688. *
  5689. * @function Highcharts.SVGRenderer#createElement
  5690. *
  5691. * @param {string} nodeName
  5692. * The node name, for example `rect`, `g` etc.
  5693. *
  5694. * @return {Highcharts.SVGElement}
  5695. * The generated SVGElement.
  5696. */
  5697. createElement: function (nodeName) {
  5698. var wrapper = new this.Element();
  5699. wrapper.init(this, nodeName);
  5700. return wrapper;
  5701. },
  5702. /**
  5703. * Dummy function for plugins, called every time the renderer is updated.
  5704. * Prior to Highcharts 5, this was used for the canvg renderer.
  5705. *
  5706. * @deprecated
  5707. * @function Highcharts.SVGRenderer#draw
  5708. */
  5709. draw: noop,
  5710. /**
  5711. * Get converted radial gradient attributes according to the radial
  5712. * reference. Used internally from the {@link SVGElement#colorGradient}
  5713. * function.
  5714. *
  5715. * @private
  5716. * @function Highcharts.SVGRenderer#getRadialAttr
  5717. *
  5718. * @param {Array<number>} radialReference
  5719. *
  5720. * @param {Highcharts.SVGAttributes} gradAttr
  5721. *
  5722. * @return {Highcharts.SVGAttributes}
  5723. */
  5724. getRadialAttr: function (radialReference, gradAttr) {
  5725. return {
  5726. cx: (radialReference[0] - radialReference[2] / 2) +
  5727. gradAttr.cx * radialReference[2],
  5728. cy: (radialReference[1] - radialReference[2] / 2) +
  5729. gradAttr.cy * radialReference[2],
  5730. r: gradAttr.r * radialReference[2]
  5731. };
  5732. },
  5733. /**
  5734. * Truncate the text node contents to a given length. Used when the css
  5735. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  5736. * character by character to the given length. If not, the text is
  5737. * word-wrapped line by line.
  5738. *
  5739. * @private
  5740. * @function Highcharts.SVGRenderer#truncate
  5741. *
  5742. * @param {Highcharts.SVGElement} wrapper
  5743. *
  5744. * @param {Highcharts.HTMLDOMElement} tspan
  5745. *
  5746. * @param {string|undefined} text
  5747. *
  5748. * @param {Array<string>|undefined} words
  5749. *
  5750. * @param {number} startAt
  5751. *
  5752. * @param {number} width
  5753. *
  5754. * @param {Function} getString
  5755. *
  5756. * @return {boolean}
  5757. * True if tspan is too long.
  5758. */
  5759. truncate: function (wrapper, tspan, text, words, startAt, width, getString) {
  5760. var renderer = this, rotation = wrapper.rotation, str,
  5761. // Word wrap can not be truncated to shorter than one word, ellipsis
  5762. // text can be completely blank.
  5763. minIndex = words ? 1 : 0, maxIndex = (text || words).length, currentIndex = maxIndex,
  5764. // Cache the lengths to avoid checking the same twice
  5765. lengths = [], updateTSpan = function (s) {
  5766. if (tspan.firstChild) {
  5767. tspan.removeChild(tspan.firstChild);
  5768. }
  5769. if (s) {
  5770. tspan.appendChild(doc.createTextNode(s));
  5771. }
  5772. }, getSubStringLength = function (charEnd, concatenatedEnd) {
  5773. // charEnd is useed when finding the character-by-character
  5774. // break for ellipsis, concatenatedEnd is used for word-by-word
  5775. // break for word wrapping.
  5776. var end = concatenatedEnd || charEnd;
  5777. if (typeof lengths[end] === 'undefined') {
  5778. // Modern browsers
  5779. if (tspan.getSubStringLength) {
  5780. // Fails with DOM exception on unit-tests/legend/members
  5781. // of unknown reason. Desired width is 0, text content
  5782. // is "5" and end is 1.
  5783. try {
  5784. lengths[end] = startAt +
  5785. tspan.getSubStringLength(0, words ? end + 1 : end);
  5786. }
  5787. catch (e) {
  5788. '';
  5789. }
  5790. // Legacy
  5791. }
  5792. else if (renderer.getSpanWidth) { // #9058 jsdom
  5793. updateTSpan(getString(text || words, charEnd));
  5794. lengths[end] = startAt +
  5795. renderer.getSpanWidth(wrapper, tspan);
  5796. }
  5797. }
  5798. return lengths[end];
  5799. }, actualWidth, truncated;
  5800. wrapper.rotation = 0; // discard rotation when computing box
  5801. actualWidth = getSubStringLength(tspan.textContent.length);
  5802. truncated = startAt + actualWidth > width;
  5803. if (truncated) {
  5804. // Do a binary search for the index where to truncate the text
  5805. while (minIndex <= maxIndex) {
  5806. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  5807. // When checking words for word-wrap, we need to build the
  5808. // string and measure the subStringLength at the concatenated
  5809. // word length.
  5810. if (words) {
  5811. str = getString(words, currentIndex);
  5812. }
  5813. actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
  5814. if (minIndex === maxIndex) {
  5815. // Complete
  5816. minIndex = maxIndex + 1;
  5817. }
  5818. else if (actualWidth > width) {
  5819. // Too large. Set max index to current.
  5820. maxIndex = currentIndex - 1;
  5821. }
  5822. else {
  5823. // Within width. Set min index to current.
  5824. minIndex = currentIndex;
  5825. }
  5826. }
  5827. // If max index was 0 it means the shortest possible text was also
  5828. // too large. For ellipsis that means only the ellipsis, while for
  5829. // word wrap it means the whole first word.
  5830. if (maxIndex === 0) {
  5831. // Remove ellipsis
  5832. updateTSpan('');
  5833. // If the new text length is one less than the original, we don't
  5834. // need the ellipsis
  5835. }
  5836. else if (!(text && maxIndex === text.length - 1)) {
  5837. updateTSpan(str || getString(text || words, currentIndex));
  5838. }
  5839. }
  5840. // When doing line wrapping, prepare for the next line by removing the
  5841. // items from this line.
  5842. if (words) {
  5843. words.splice(0, currentIndex);
  5844. }
  5845. wrapper.actualWidth = actualWidth;
  5846. wrapper.rotation = rotation; // Apply rotation again.
  5847. return truncated;
  5848. },
  5849. /**
  5850. * A collection of characters mapped to HTML entities. When `useHTML` on an
  5851. * element is true, these entities will be rendered correctly by HTML. In
  5852. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  5853. * so for example `&lt;` will render as `<`.
  5854. *
  5855. * @example
  5856. * // Add support for unescaping quotes
  5857. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  5858. *
  5859. * @name Highcharts.SVGRenderer#escapes
  5860. * @type {Highcharts.Dictionary<string>}
  5861. */
  5862. escapes: {
  5863. '&': '&amp;',
  5864. '<': '&lt;',
  5865. '>': '&gt;',
  5866. "'": '&#39;',
  5867. '"': '&quot;'
  5868. },
  5869. /**
  5870. * Parse a simple HTML string into SVG tspans. Called internally when text
  5871. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  5872. * text features like `width`, `text-overflow`, `white-space`, and also
  5873. * attributes like `href` and `style`.
  5874. *
  5875. * @private
  5876. * @function Highcharts.SVGRenderer#buildText
  5877. *
  5878. * @param {Highcharts.SVGElement} wrapper
  5879. * The parent SVGElement.
  5880. *
  5881. * @return {void}
  5882. */
  5883. buildText: function (wrapper) {
  5884. 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) {
  5885. var fontSizeStyle;
  5886. if (!renderer.styledMode) {
  5887. fontSizeStyle =
  5888. /(px|em)$/.test(tspan && tspan.style.fontSize) ?
  5889. tspan.style.fontSize :
  5890. (fontSize || renderer.style.fontSize || 12);
  5891. }
  5892. return textLineHeight ?
  5893. pInt(textLineHeight) :
  5894. renderer.fontMetrics(fontSizeStyle,
  5895. // Get the computed size from parent if not explicit
  5896. (tspan.getAttribute('style') ? tspan : textNode)).h;
  5897. }, unescapeEntities = function (inputStr, except) {
  5898. objectEach(renderer.escapes, function (value, key) {
  5899. if (!except || except.indexOf(value) === -1) {
  5900. inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
  5901. }
  5902. });
  5903. return inputStr;
  5904. }, parseAttribute = function (s, attr) {
  5905. var start, delimiter;
  5906. start = s.indexOf('<');
  5907. s = s.substring(start, s.indexOf('>') - start);
  5908. start = s.indexOf(attr + '=');
  5909. if (start !== -1) {
  5910. start = start + attr.length + 1;
  5911. delimiter = s.charAt(start);
  5912. if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes
  5913. s = s.substring(start + 1);
  5914. return s.substring(0, s.indexOf(delimiter));
  5915. }
  5916. }
  5917. };
  5918. var regexMatchBreaks = /<br.*?>/g;
  5919. // The buildText code is quite heavy, so if we're not changing something
  5920. // that affects the text, skip it (#6113).
  5921. textCache = [
  5922. textStr,
  5923. ellipsis,
  5924. noWrap,
  5925. textLineHeight,
  5926. textOutline,
  5927. fontSize,
  5928. width
  5929. ].join(',');
  5930. if (textCache === wrapper.textCache) {
  5931. return;
  5932. }
  5933. wrapper.textCache = textCache;
  5934. // Remove old text
  5935. while (i--) {
  5936. textNode.removeChild(childNodes[i]);
  5937. }
  5938. // Skip tspans, add text directly to text node. The forceTSpan is a hook
  5939. // used in text outline hack.
  5940. if (!hasMarkup &&
  5941. !textOutline &&
  5942. !ellipsis &&
  5943. !width &&
  5944. (textStr.indexOf(' ') === -1 ||
  5945. (noWrap && !regexMatchBreaks.test(textStr)))) {
  5946. textNode.appendChild(doc.createTextNode(unescapeEntities(textStr)));
  5947. // Complex strings, add more logic
  5948. }
  5949. else {
  5950. if (tempParent) {
  5951. // attach it to the DOM to read offset width
  5952. tempParent.appendChild(textNode);
  5953. }
  5954. if (hasMarkup) {
  5955. lines = renderer.styledMode ? (textStr
  5956. .replace(/<(b|strong)>/g, '<span class="highcharts-strong">')
  5957. .replace(/<(i|em)>/g, '<span class="highcharts-emphasized">')) : (textStr
  5958. .replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
  5959. .replace(/<(i|em)>/g, '<span style="font-style:italic">'));
  5960. lines = lines
  5961. .replace(/<a/g, '<span')
  5962. .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
  5963. .split(regexMatchBreaks);
  5964. }
  5965. else {
  5966. lines = [textStr];
  5967. }
  5968. // Trim empty lines (#5261)
  5969. lines = lines.filter(function (line) {
  5970. return line !== '';
  5971. });
  5972. // build the lines
  5973. lines.forEach(function (line, lineNo) {
  5974. var spans, spanNo = 0, lineLength = 0;
  5975. line = line
  5976. // Trim to prevent useless/costly process on the spaces
  5977. // (#5258)
  5978. .replace(/^\s+|\s+$/g, '')
  5979. .replace(/<span/g, '|||<span')
  5980. .replace(/<\/span>/g, '</span>|||');
  5981. spans = line.split('|||');
  5982. spans.forEach(function buildTextSpans(span) {
  5983. if (span !== '' || spans.length === 1) {
  5984. var attributes = {}, tspan = doc.createElementNS(renderer.SVG_NS, 'tspan'), classAttribute, styleAttribute, // #390
  5985. hrefAttribute;
  5986. classAttribute = parseAttribute(span, 'class');
  5987. if (classAttribute) {
  5988. attr(tspan, 'class', classAttribute);
  5989. }
  5990. styleAttribute = parseAttribute(span, 'style');
  5991. if (styleAttribute) {
  5992. styleAttribute = styleAttribute.replace(/(;| |^)color([ :])/, '$1fill$2');
  5993. attr(tspan, 'style', styleAttribute);
  5994. }
  5995. // Not for export - #1529
  5996. hrefAttribute = parseAttribute(span, 'href');
  5997. if (hrefAttribute && !forExport) {
  5998. attr(tspan, 'onclick', 'location.href=\"' + hrefAttribute + '\"');
  5999. attr(tspan, 'class', 'highcharts-anchor');
  6000. if (!renderer.styledMode) {
  6001. css(tspan, { cursor: 'pointer' });
  6002. }
  6003. }
  6004. // Strip away unsupported HTML tags (#7126)
  6005. span = unescapeEntities(span.replace(/<[a-zA-Z\/](.|\n)*?>/g, '') || ' ');
  6006. // Nested tags aren't supported, and cause crash in
  6007. // Safari (#1596)
  6008. if (span !== ' ') {
  6009. // add the text node
  6010. tspan.appendChild(doc.createTextNode(span));
  6011. // First span in a line, align it to the left
  6012. if (!spanNo) {
  6013. if (lineNo && parentX !== null) {
  6014. attributes.x = parentX;
  6015. }
  6016. }
  6017. else {
  6018. attributes.dx = 0; // #16
  6019. }
  6020. // add attributes
  6021. attr(tspan, attributes);
  6022. // Append it
  6023. textNode.appendChild(tspan);
  6024. // first span on subsequent line, add the line
  6025. // height
  6026. if (!spanNo && isSubsequentLine) {
  6027. // allow getting the right offset height in
  6028. // exporting in IE
  6029. if (!svg && forExport) {
  6030. css(tspan, { display: 'block' });
  6031. }
  6032. // Set the line height based on the font size of
  6033. // either the text element or the tspan element
  6034. attr(tspan, 'dy', getLineHeight(tspan));
  6035. }
  6036. // Check width and apply soft breaks or ellipsis
  6037. if (width) {
  6038. var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
  6039. hasWhiteSpace = !noWrap && (spans.length > 1 ||
  6040. lineNo ||
  6041. words.length > 1), wrapLineNo = 0, dy = getLineHeight(tspan);
  6042. if (ellipsis) {
  6043. truncated = renderer.truncate(wrapper, tspan, span, void 0, 0,
  6044. // Target width
  6045. Math.max(0,
  6046. // Substract the font face to make
  6047. // room for the ellipsis itself
  6048. width - parseInt(fontSize || 12, 10)),
  6049. // Build the text to test for
  6050. function (text, currentIndex) {
  6051. return text.substring(0, currentIndex) + '\u2026';
  6052. });
  6053. }
  6054. else if (hasWhiteSpace) {
  6055. while (words.length) {
  6056. // For subsequent lines, create tspans
  6057. // with the same style attributes as the
  6058. // parent text node.
  6059. if (words.length &&
  6060. !noWrap &&
  6061. wrapLineNo > 0) {
  6062. tspan = doc.createElementNS(SVG_NS, 'tspan');
  6063. attr(tspan, {
  6064. dy: dy,
  6065. x: parentX
  6066. });
  6067. if (styleAttribute) { // #390
  6068. attr(tspan, 'style', styleAttribute);
  6069. }
  6070. // Start by appending the full
  6071. // remaining text
  6072. tspan.appendChild(doc.createTextNode(words.join(' ')
  6073. .replace(/- /g, '-')));
  6074. textNode.appendChild(tspan);
  6075. }
  6076. // For each line, truncate the remaining
  6077. // words into the line length.
  6078. renderer.truncate(wrapper, tspan, null, words, wrapLineNo === 0 ? lineLength : 0, width,
  6079. // Build the text to test for
  6080. function (text, currentIndex) {
  6081. return words
  6082. .slice(0, currentIndex)
  6083. .join(' ')
  6084. .replace(/- /g, '-');
  6085. });
  6086. lineLength = wrapper.actualWidth;
  6087. wrapLineNo++;
  6088. }
  6089. }
  6090. }
  6091. spanNo++;
  6092. }
  6093. }
  6094. });
  6095. // To avoid beginning lines that doesn't add to the textNode
  6096. // (#6144)
  6097. isSubsequentLine = (isSubsequentLine ||
  6098. textNode.childNodes.length);
  6099. });
  6100. if (ellipsis && truncated) {
  6101. wrapper.attr('title', unescapeEntities(wrapper.textStr, ['&lt;', '&gt;']) // #7179
  6102. );
  6103. }
  6104. if (tempParent) {
  6105. tempParent.removeChild(textNode);
  6106. }
  6107. // Apply the text outline
  6108. if (textOutline && wrapper.applyTextOutline) {
  6109. wrapper.applyTextOutline(textOutline);
  6110. }
  6111. }
  6112. },
  6113. /**
  6114. * Returns white for dark colors and black for bright colors.
  6115. *
  6116. * @function Highcharts.SVGRenderer#getContrast
  6117. *
  6118. * @param {Highcharts.ColorString} rgba
  6119. * The color to get the contrast for.
  6120. *
  6121. * @return {Highcharts.ColorString}
  6122. * The contrast color, either `#000000` or `#FFFFFF`.
  6123. */
  6124. getContrast: function (rgba) {
  6125. rgba = color(rgba).rgba;
  6126. // The threshold may be discussed. Here's a proposal for adding
  6127. // different weight to the color channels (#6216)
  6128. rgba[0] *= 1; // red
  6129. rgba[1] *= 1.2; // green
  6130. rgba[2] *= 0.5; // blue
  6131. return rgba[0] + rgba[1] + rgba[2] >
  6132. 1.8 * 255 ?
  6133. '#000000' :
  6134. '#FFFFFF';
  6135. },
  6136. /**
  6137. * Create a button with preset states.
  6138. *
  6139. * @function Highcharts.SVGRenderer#button
  6140. *
  6141. * @param {string} text
  6142. * The text or HTML to draw.
  6143. *
  6144. * @param {number} x
  6145. * The x position of the button's left side.
  6146. *
  6147. * @param {number} y
  6148. * The y position of the button's top side.
  6149. *
  6150. * @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
  6151. * The function to execute on button click or touch.
  6152. *
  6153. * @param {Highcharts.SVGAttributes} [normalState]
  6154. * SVG attributes for the normal state.
  6155. *
  6156. * @param {Highcharts.SVGAttributes} [hoverState]
  6157. * SVG attributes for the hover state.
  6158. *
  6159. * @param {Highcharts.SVGAttributes} [pressedState]
  6160. * SVG attributes for the pressed state.
  6161. *
  6162. * @param {Highcharts.SVGAttributes} [disabledState]
  6163. * SVG attributes for the disabled state.
  6164. *
  6165. * @param {Highcharts.SymbolKeyValue} [shape=rect]
  6166. * The shape type.
  6167. *
  6168. * @param {boolean} [useHTML=false]
  6169. * Wether to use HTML to render the label.
  6170. *
  6171. * @return {Highcharts.SVGElement}
  6172. * The button element.
  6173. */
  6174. button: function (text, x, y, callback, normalState, hoverState, pressedState, disabledState, shape, useHTML) {
  6175. var label = this.label(text, x, y, shape, null, null, useHTML, null, 'button'), curState = 0, styledMode = this.styledMode;
  6176. // Default, non-stylable attributes
  6177. label.attr(merge({ padding: 8, r: 2 }, normalState));
  6178. if (!styledMode) {
  6179. // Presentational
  6180. var normalStyle, hoverStyle, pressedStyle, disabledStyle;
  6181. // Normal state - prepare the attributes
  6182. normalState = merge({
  6183. fill: '#f7f7f7',
  6184. stroke: '#cccccc',
  6185. 'stroke-width': 1,
  6186. style: {
  6187. color: '#333333',
  6188. cursor: 'pointer',
  6189. fontWeight: 'normal'
  6190. }
  6191. }, normalState);
  6192. normalStyle = normalState.style;
  6193. delete normalState.style;
  6194. // Hover state
  6195. hoverState = merge(normalState, {
  6196. fill: '#e6e6e6'
  6197. }, hoverState);
  6198. hoverStyle = hoverState.style;
  6199. delete hoverState.style;
  6200. // Pressed state
  6201. pressedState = merge(normalState, {
  6202. fill: '#e6ebf5',
  6203. style: {
  6204. color: '#000000',
  6205. fontWeight: 'bold'
  6206. }
  6207. }, pressedState);
  6208. pressedStyle = pressedState.style;
  6209. delete pressedState.style;
  6210. // Disabled state
  6211. disabledState = merge(normalState, {
  6212. style: {
  6213. color: '#cccccc'
  6214. }
  6215. }, disabledState);
  6216. disabledStyle = disabledState.style;
  6217. delete disabledState.style;
  6218. }
  6219. // Add the events. IE9 and IE10 need mouseover and mouseout to funciton
  6220. // (#667).
  6221. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  6222. if (curState !== 3) {
  6223. label.setState(1);
  6224. }
  6225. });
  6226. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  6227. if (curState !== 3) {
  6228. label.setState(curState);
  6229. }
  6230. });
  6231. label.setState = function (state) {
  6232. // Hover state is temporary, don't record it
  6233. if (state !== 1) {
  6234. label.state = curState = state;
  6235. }
  6236. // Update visuals
  6237. label
  6238. .removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
  6239. .addClass('highcharts-button-' +
  6240. ['normal', 'hover', 'pressed', 'disabled'][state || 0]);
  6241. if (!styledMode) {
  6242. label
  6243. .attr([
  6244. normalState,
  6245. hoverState,
  6246. pressedState,
  6247. disabledState
  6248. ][state || 0])
  6249. .css([
  6250. normalStyle,
  6251. hoverStyle,
  6252. pressedStyle,
  6253. disabledStyle
  6254. ][state || 0]);
  6255. }
  6256. };
  6257. // Presentational attributes
  6258. if (!styledMode) {
  6259. label
  6260. .attr(normalState)
  6261. .css(extend({ cursor: 'default' }, normalStyle));
  6262. }
  6263. return label
  6264. .on('click', function (e) {
  6265. if (curState !== 3) {
  6266. callback.call(label, e);
  6267. }
  6268. });
  6269. },
  6270. /**
  6271. * Make a straight line crisper by not spilling out to neighbour pixels.
  6272. *
  6273. * @function Highcharts.SVGRenderer#crispLine
  6274. *
  6275. * @param {Highcharts.SVGPathArray} points
  6276. * The original points on the format `['M', 0, 0, 'L', 100, 0]`.
  6277. *
  6278. * @param {number} width
  6279. * The width of the line.
  6280. *
  6281. * @return {Highcharts.SVGPathArray}
  6282. * The original points array, but modified to render crisply.
  6283. */
  6284. crispLine: function (points, width) {
  6285. // normalize to a crisp line
  6286. if (points[1] === points[4]) {
  6287. // Substract due to #1129. Now bottom and left axis gridlines behave
  6288. // the same.
  6289. points[1] = points[4] =
  6290. Math.round(points[1]) - (width % 2 / 2);
  6291. }
  6292. if (points[2] === points[5]) {
  6293. points[2] = points[5] =
  6294. Math.round(points[2]) + (width % 2 / 2);
  6295. }
  6296. return points;
  6297. },
  6298. /**
  6299. * Draw a path, wraps the SVG `path` element.
  6300. *
  6301. * @sample highcharts/members/renderer-path-on-chart/
  6302. * Draw a path in a chart
  6303. * @sample highcharts/members/renderer-path/
  6304. * Draw a path independent from a chart
  6305. *
  6306. * @example
  6307. * var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  6308. * .attr({ stroke: '#ff00ff' })
  6309. * .add();
  6310. *
  6311. * @function Highcharts.SVGRenderer#path
  6312. *
  6313. * @param {Highcharts.SVGPathArray} [path]
  6314. * An SVG path definition in array form.
  6315. *
  6316. * @return {Highcharts.SVGElement}
  6317. * The generated wrapper element.
  6318. *
  6319. */ /**
  6320. * Draw a path, wraps the SVG `path` element.
  6321. *
  6322. * @function Highcharts.SVGRenderer#path
  6323. *
  6324. * @param {Highcharts.SVGAttributes} [attribs]
  6325. * The initial attributes.
  6326. *
  6327. * @return {Highcharts.SVGElement}
  6328. * The generated wrapper element.
  6329. */
  6330. path: function (path) {
  6331. var attribs = (this.styledMode ? {} : {
  6332. fill: 'none'
  6333. });
  6334. if (isArray(path)) {
  6335. attribs.d = path;
  6336. }
  6337. else if (isObject(path)) { // attributes
  6338. extend(attribs, path);
  6339. }
  6340. return this.createElement('path').attr(attribs);
  6341. },
  6342. /**
  6343. * Draw a circle, wraps the SVG `circle` element.
  6344. *
  6345. * @sample highcharts/members/renderer-circle/
  6346. * Drawing a circle
  6347. *
  6348. * @function Highcharts.SVGRenderer#circle
  6349. *
  6350. * @param {number} [x]
  6351. * The center x position.
  6352. *
  6353. * @param {number} [y]
  6354. * The center y position.
  6355. *
  6356. * @param {number} [r]
  6357. * The radius.
  6358. *
  6359. * @return {Highcharts.SVGElement}
  6360. * The generated wrapper element.
  6361. */ /**
  6362. * Draw a circle, wraps the SVG `circle` element.
  6363. *
  6364. * @function Highcharts.SVGRenderer#circle
  6365. *
  6366. * @param {Highcharts.SVGAttributes} [attribs]
  6367. * The initial attributes.
  6368. *
  6369. * @return {Highcharts.SVGElement}
  6370. * The generated wrapper element.
  6371. */
  6372. circle: function (x, y, r) {
  6373. var attribs = (isObject(x) ?
  6374. x :
  6375. typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
  6376. // Setting x or y translates to cx and cy
  6377. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  6378. element.setAttribute('c' + key, value);
  6379. };
  6380. return wrapper.attr(attribs);
  6381. },
  6382. /**
  6383. * Draw and return an arc.
  6384. *
  6385. * @sample highcharts/members/renderer-arc/
  6386. * Drawing an arc
  6387. *
  6388. * @function Highcharts.SVGRenderer#arc
  6389. *
  6390. * @param {number} [x=0]
  6391. * Center X position.
  6392. *
  6393. * @param {number} [y=0]
  6394. * Center Y position.
  6395. *
  6396. * @param {number} [r=0]
  6397. * The outer radius' of the arc.
  6398. *
  6399. * @param {number} [innerR=0]
  6400. * Inner radius like used in donut charts.
  6401. *
  6402. * @param {number} [start=0]
  6403. * The starting angle of the arc in radians, where 0 is to the right
  6404. * and `-Math.PI/2` is up.
  6405. *
  6406. * @param {number} [end=0]
  6407. * The ending angle of the arc in radians, where 0 is to the right
  6408. * and `-Math.PI/2` is up.
  6409. *
  6410. * @return {Highcharts.SVGElement}
  6411. * The generated wrapper element.
  6412. */ /**
  6413. * Draw and return an arc. Overloaded function that takes arguments object.
  6414. *
  6415. * @function Highcharts.SVGRenderer#arc
  6416. *
  6417. * @param {Highcharts.SVGAttributes} attribs
  6418. * Initial SVG attributes.
  6419. *
  6420. * @return {Highcharts.SVGElement}
  6421. * The generated wrapper element.
  6422. */
  6423. arc: function (x, y, r, innerR, start, end) {
  6424. var arc, options;
  6425. if (isObject(x)) {
  6426. options = x;
  6427. y = options.y;
  6428. r = options.r;
  6429. innerR = options.innerR;
  6430. start = options.start;
  6431. end = options.end;
  6432. x = options.x;
  6433. }
  6434. else {
  6435. options = {
  6436. innerR: innerR,
  6437. start: start,
  6438. end: end
  6439. };
  6440. }
  6441. // Arcs are defined as symbols for the ability to set
  6442. // attributes in attr and animate
  6443. arc = this.symbol('arc', x, y, r, r, options);
  6444. arc.r = r; // #959
  6445. return arc;
  6446. },
  6447. /**
  6448. * Draw and return a rectangle.
  6449. *
  6450. * @function Highcharts.SVGRenderer#rect
  6451. *
  6452. * @param {number} [x]
  6453. * Left position.
  6454. *
  6455. * @param {number} [y]
  6456. * Top position.
  6457. *
  6458. * @param {number} [width]
  6459. * Width of the rectangle.
  6460. *
  6461. * @param {number} [height]
  6462. * Height of the rectangle.
  6463. *
  6464. * @param {number} [r]
  6465. * Border corner radius.
  6466. *
  6467. * @param {number} [strokeWidth]
  6468. * A stroke width can be supplied to allow crisp drawing.
  6469. *
  6470. * @return {Highcharts.SVGElement}
  6471. * The generated wrapper element.
  6472. */ /**
  6473. * Draw and return a rectangle.
  6474. *
  6475. * @sample highcharts/members/renderer-rect-on-chart/
  6476. * Draw a rectangle in a chart
  6477. * @sample highcharts/members/renderer-rect/
  6478. * Draw a rectangle independent from a chart
  6479. *
  6480. * @function Highcharts.SVGRenderer#rect
  6481. *
  6482. * @param {Highcharts.SVGAttributes} [attributes]
  6483. * General SVG attributes for the rectangle.
  6484. *
  6485. * @return {Highcharts.SVGElement}
  6486. * The generated wrapper element.
  6487. */
  6488. rect: function (x, y, width, height, r, strokeWidth) {
  6489. r = isObject(x) ? x.r : r;
  6490. var wrapper = this.createElement('rect'), attribs = isObject(x) ?
  6491. x :
  6492. typeof x === 'undefined' ?
  6493. {} :
  6494. {
  6495. x: x,
  6496. y: y,
  6497. width: Math.max(width, 0),
  6498. height: Math.max(height, 0)
  6499. };
  6500. if (!this.styledMode) {
  6501. if (typeof strokeWidth !== 'undefined') {
  6502. attribs.strokeWidth = strokeWidth;
  6503. attribs = wrapper.crisp(attribs);
  6504. }
  6505. attribs.fill = 'none';
  6506. }
  6507. if (r) {
  6508. attribs.r = r;
  6509. }
  6510. wrapper.rSetter = function (value, key, element) {
  6511. wrapper.r = value;
  6512. attr(element, {
  6513. rx: value,
  6514. ry: value
  6515. });
  6516. };
  6517. wrapper.rGetter = function () {
  6518. return wrapper.r;
  6519. };
  6520. return wrapper.attr(attribs);
  6521. },
  6522. /**
  6523. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  6524. * elements.
  6525. *
  6526. * @sample highcharts/members/renderer-g/
  6527. * Show and hide grouped objects
  6528. *
  6529. * @function Highcharts.SVGRenderer#setSize
  6530. *
  6531. * @param {number} width
  6532. * The new pixel width.
  6533. *
  6534. * @param {number} height
  6535. * The new pixel height.
  6536. *
  6537. * @param {boolean|Highcharts.AnimationOptionsObject} [animate=true]
  6538. * Whether and how to animate.
  6539. *
  6540. * @return {void}
  6541. */
  6542. setSize: function (width, height, animate) {
  6543. var renderer = this, alignedObjects = renderer.alignedObjects, i = alignedObjects.length;
  6544. renderer.width = width;
  6545. renderer.height = height;
  6546. renderer.boxWrapper.animate({
  6547. width: width,
  6548. height: height
  6549. }, {
  6550. step: function () {
  6551. this.attr({
  6552. viewBox: '0 0 ' + this.attr('width') + ' ' +
  6553. this.attr('height')
  6554. });
  6555. },
  6556. duration: pick(animate, true) ? void 0 : 0
  6557. });
  6558. while (i--) {
  6559. alignedObjects[i].align();
  6560. }
  6561. },
  6562. /**
  6563. * Create and return an svg group element. Child
  6564. * {@link Highcharts.SVGElement} objects are added to the group by using the
  6565. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  6566. *
  6567. * @function Highcharts.SVGRenderer#g
  6568. *
  6569. * @param {string} [name]
  6570. * The group will be given a class name of `highcharts-{name}`. This
  6571. * can be used for styling and scripting.
  6572. *
  6573. * @return {Highcharts.SVGElement}
  6574. * The generated wrapper element.
  6575. */
  6576. g: function (name) {
  6577. var elem = this.createElement('g');
  6578. return name ?
  6579. elem.attr({ 'class': 'highcharts-' + name }) :
  6580. elem;
  6581. },
  6582. /**
  6583. * Display an image.
  6584. *
  6585. * @sample highcharts/members/renderer-image-on-chart/
  6586. * Add an image in a chart
  6587. * @sample highcharts/members/renderer-image/
  6588. * Add an image independent of a chart
  6589. *
  6590. * @function Highcharts.SVGRenderer#image
  6591. *
  6592. * @param {string} src
  6593. * The image source.
  6594. *
  6595. * @param {number} [x]
  6596. * The X position.
  6597. *
  6598. * @param {number} [y]
  6599. * The Y position.
  6600. *
  6601. * @param {number} [width]
  6602. * The image width. If omitted, it defaults to the image file width.
  6603. *
  6604. * @param {number} [height]
  6605. * The image height. If omitted it defaults to the image file
  6606. * height.
  6607. *
  6608. * @param {Function} [onload]
  6609. * Event handler for image load.
  6610. *
  6611. * @return {Highcharts.SVGElement}
  6612. * The generated wrapper element.
  6613. */
  6614. image: function (src, x, y, width, height, onload) {
  6615. var attribs = { preserveAspectRatio: 'none' }, elemWrapper, dummy, setSVGImageSource = function (el, src) {
  6616. // Set the href in the xlink namespace
  6617. if (el.setAttributeNS) {
  6618. el.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
  6619. }
  6620. else {
  6621. // could be exporting in IE
  6622. // using href throws "not supported" in ie7 and under,
  6623. // requries regex shim to fix later
  6624. el.setAttribute('hc-svg-href', src);
  6625. }
  6626. }, onDummyLoad = function (e) {
  6627. setSVGImageSource(elemWrapper.element, src);
  6628. onload.call(elemWrapper, e);
  6629. };
  6630. // optional properties
  6631. if (arguments.length > 1) {
  6632. extend(attribs, {
  6633. x: x,
  6634. y: y,
  6635. width: width,
  6636. height: height
  6637. });
  6638. }
  6639. elemWrapper = this.createElement('image').attr(attribs);
  6640. // Add load event if supplied
  6641. if (onload) {
  6642. // We have to use a dummy HTML image since IE support for SVG image
  6643. // load events is very buggy. First set a transparent src, wait for
  6644. // dummy to load, and then add the real src to the SVG image.
  6645. setSVGImageSource(elemWrapper.element, 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* eslint-disable-line */);
  6646. dummy = new win.Image();
  6647. addEvent(dummy, 'load', onDummyLoad);
  6648. dummy.src = src;
  6649. if (dummy.complete) {
  6650. onDummyLoad({});
  6651. }
  6652. }
  6653. else {
  6654. setSVGImageSource(elemWrapper.element, src);
  6655. }
  6656. return elemWrapper;
  6657. },
  6658. /**
  6659. * Draw a symbol out of pre-defined shape paths from
  6660. * {@link SVGRenderer#symbols}.
  6661. * It is used in Highcharts for point makers, which cake a `symbol` option,
  6662. * and label and button backgrounds like in the tooltip and stock flags.
  6663. *
  6664. * @function Highcharts.SVGRenderer#symbol
  6665. *
  6666. * @param {string} symbol
  6667. * The symbol name.
  6668. *
  6669. * @param {number} [x]
  6670. * The X coordinate for the top left position.
  6671. *
  6672. * @param {number} [y]
  6673. * The Y coordinate for the top left position.
  6674. *
  6675. * @param {number} [width]
  6676. * The pixel width.
  6677. *
  6678. * @param {number} [height]
  6679. * The pixel height.
  6680. *
  6681. * @param {Highcharts.SymbolOptionsObject} [options]
  6682. * Additional options, depending on the actual symbol drawn.
  6683. *
  6684. * @return {Highcharts.SVGElement}
  6685. */
  6686. symbol: function (symbol, x, y, width, height, options) {
  6687. var ren = this, obj, imageRegex = /^url\((.*?)\)$/, isImage = imageRegex.test(symbol), sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
  6688. // get the symbol definition function
  6689. symbolFn = (sym && this.symbols[sym]),
  6690. // check if there's a path defined for this symbol
  6691. path = (defined(x) && symbolFn && symbolFn.call(this.symbols, Math.round(x), Math.round(y), width, height, options)), imageSrc, centerImage;
  6692. if (symbolFn) {
  6693. obj = this.path(path);
  6694. if (!ren.styledMode) {
  6695. obj.attr('fill', 'none');
  6696. }
  6697. // expando properties for use in animate and attr
  6698. extend(obj, {
  6699. symbolName: sym,
  6700. x: x,
  6701. y: y,
  6702. width: width,
  6703. height: height
  6704. });
  6705. if (options) {
  6706. extend(obj, options);
  6707. }
  6708. // Image symbols
  6709. }
  6710. else if (isImage) {
  6711. imageSrc = symbol.match(imageRegex)[1];
  6712. // Create the image synchronously, add attribs async
  6713. obj = this.image(imageSrc);
  6714. // The image width is not always the same as the symbol width. The
  6715. // image may be centered within the symbol, as is the case when
  6716. // image shapes are used as label backgrounds, for example in flags.
  6717. obj.imgwidth = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].width, options && options.width);
  6718. obj.imgheight = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].height, options && options.height);
  6719. /**
  6720. * Set the size and position
  6721. */
  6722. centerImage = function () {
  6723. obj.attr({
  6724. width: obj.width,
  6725. height: obj.height
  6726. });
  6727. };
  6728. /**
  6729. * Width and height setters that take both the image's physical size
  6730. * and the label size into consideration, and translates the image
  6731. * to center within the label.
  6732. */
  6733. ['width', 'height'].forEach(function (key) {
  6734. obj[key + 'Setter'] = function (value, key) {
  6735. var attribs = {}, imgSize = this['img' + key], trans = key === 'width' ? 'translateX' : 'translateY';
  6736. this[key] = value;
  6737. if (defined(imgSize)) {
  6738. // Scale and center the image within its container.
  6739. // The name `backgroundSize` is taken from the CSS spec,
  6740. // but the value `within` is made up. Other possible
  6741. // values in the spec, `cover` and `contain`, can be
  6742. // implemented if needed.
  6743. if (options &&
  6744. options.backgroundSize === 'within' &&
  6745. this.width &&
  6746. this.height) {
  6747. imgSize = Math.round(imgSize * Math.min(this.width / this.imgwidth, this.height / this.imgheight));
  6748. }
  6749. if (this.element) {
  6750. this.element.setAttribute(key, imgSize);
  6751. }
  6752. if (!this.alignByTranslate) {
  6753. attribs[trans] = ((this[key] || 0) - imgSize) / 2;
  6754. this.attr(attribs);
  6755. }
  6756. }
  6757. };
  6758. });
  6759. if (defined(x)) {
  6760. obj.attr({
  6761. x: x,
  6762. y: y
  6763. });
  6764. }
  6765. obj.isImg = true;
  6766. if (defined(obj.imgwidth) && defined(obj.imgheight)) {
  6767. centerImage();
  6768. }
  6769. else {
  6770. // Initialize image to be 0 size so export will still function
  6771. // if there's no cached sizes.
  6772. obj.attr({ width: 0, height: 0 });
  6773. // Create a dummy JavaScript image to get the width and height.
  6774. createElement('img', {
  6775. onload: function () {
  6776. var chart = charts[ren.chartIndex];
  6777. // Special case for SVGs on IE11, the width is not
  6778. // accessible until the image is part of the DOM
  6779. // (#2854).
  6780. if (this.width === 0) {
  6781. css(this, {
  6782. position: 'absolute',
  6783. top: '-999em'
  6784. });
  6785. doc.body.appendChild(this);
  6786. }
  6787. // Center the image
  6788. symbolSizes[imageSrc] = {
  6789. width: this.width,
  6790. height: this.height
  6791. };
  6792. obj.imgwidth = this.width;
  6793. obj.imgheight = this.height;
  6794. if (obj.element) {
  6795. centerImage();
  6796. }
  6797. // Clean up after #2854 workaround.
  6798. if (this.parentNode) {
  6799. this.parentNode.removeChild(this);
  6800. }
  6801. // Fire the load event when all external images are
  6802. // loaded
  6803. ren.imgCount--;
  6804. if (!ren.imgCount && chart && chart.onload) {
  6805. chart.onload();
  6806. }
  6807. },
  6808. src: imageSrc
  6809. });
  6810. this.imgCount++;
  6811. }
  6812. }
  6813. return obj;
  6814. },
  6815. /**
  6816. * An extendable collection of functions for defining symbol paths.
  6817. *
  6818. * @name Highcharts.SVGRenderer#symbols
  6819. * @type {Highcharts.SymbolDictionary}
  6820. */
  6821. symbols: {
  6822. circle: function (x, y, w, h) {
  6823. // Return a full arc
  6824. return this.arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  6825. start: Math.PI * 0.5,
  6826. end: Math.PI * 2.5,
  6827. open: false
  6828. });
  6829. },
  6830. square: function (x, y, w, h) {
  6831. return [
  6832. 'M', x, y,
  6833. 'L', x + w, y,
  6834. x + w, y + h,
  6835. x, y + h,
  6836. 'Z'
  6837. ];
  6838. },
  6839. triangle: function (x, y, w, h) {
  6840. return [
  6841. 'M', x + w / 2, y,
  6842. 'L', x + w, y + h,
  6843. x, y + h,
  6844. 'Z'
  6845. ];
  6846. },
  6847. 'triangle-down': function (x, y, w, h) {
  6848. return [
  6849. 'M', x, y,
  6850. 'L', x + w, y,
  6851. x + w / 2, y + h,
  6852. 'Z'
  6853. ];
  6854. },
  6855. diamond: function (x, y, w, h) {
  6856. return [
  6857. 'M', x + w / 2, y,
  6858. 'L', x + w, y + h / 2,
  6859. x + w / 2, y + h,
  6860. x, y + h / 2,
  6861. 'Z'
  6862. ];
  6863. },
  6864. arc: function (x, y, w, h, options) {
  6865. var start = options.start, rx = options.r || w, ry = options.r || h || w, proximity = 0.001, fullCircle = Math.abs(options.end - options.start - 2 * Math.PI) <
  6866. proximity,
  6867. // Substract a small number to prevent cos and sin of start and
  6868. // end from becoming equal on 360 arcs (related: #1561)
  6869. end = options.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),
  6870. // Proximity takes care of rounding errors around PI (#6971)
  6871. longArc = pick(options.longArc, options.end - start - Math.PI < proximity ? 0 : 1), arc;
  6872. arc = [
  6873. 'M',
  6874. x + rx * cosStart,
  6875. y + ry * sinStart,
  6876. 'A',
  6877. rx,
  6878. ry,
  6879. 0,
  6880. longArc,
  6881. pick(options.clockwise, 1),
  6882. x + rx * cosEnd,
  6883. y + ry * sinEnd
  6884. ];
  6885. if (defined(innerRadius)) {
  6886. arc.push(open ? 'M' : 'L', x + innerRadius * cosEnd, y + innerRadius * sinEnd, 'A', // arcTo
  6887. innerRadius, // x radius
  6888. innerRadius, // y radius
  6889. 0, // slanting
  6890. longArc, // long or short arc
  6891. // Clockwise - opposite to the outer arc clockwise
  6892. defined(options.clockwise) ? 1 - options.clockwise : 0, x + innerRadius * cosStart, y + innerRadius * sinStart);
  6893. }
  6894. arc.push(open ? '' : 'Z'); // close
  6895. return arc;
  6896. },
  6897. /**
  6898. * Callout shape used for default tooltips, also used for rounded
  6899. * rectangles in VML
  6900. */
  6901. callout: function (x, y, w, h, options) {
  6902. var arrowLength = 6, halfDistance = 6, r = Math.min((options && options.r) || 0, w, h), safeDistance = r + halfDistance, anchorX = options && options.anchorX, anchorY = options && options.anchorY, path;
  6903. path = [
  6904. 'M', x + r, y,
  6905. 'L', x + w - r, y,
  6906. 'C', x + w, y, x + w, y, x + w, y + r,
  6907. 'L', x + w, y + h - r,
  6908. 'C', x + w, y + h, x + w, y + h, x + w - r, y + h,
  6909. 'L', x + r, y + h,
  6910. 'C', x, y + h, x, y + h, x, y + h - r,
  6911. 'L', x, y + r,
  6912. 'C', x, y, x, y, x + r, y // top-left corner
  6913. ];
  6914. // Anchor on right side
  6915. if (anchorX && anchorX > w) {
  6916. // Chevron
  6917. if (anchorY > y + safeDistance &&
  6918. anchorY < y + h - safeDistance) {
  6919. path.splice(13, 3, 'L', x + w, anchorY - halfDistance, x + w + arrowLength, anchorY, x + w, anchorY + halfDistance, x + w, y + h - r);
  6920. // Simple connector
  6921. }
  6922. else {
  6923. path.splice(13, 3, 'L', x + w, h / 2, anchorX, anchorY, x + w, h / 2, x + w, y + h - r);
  6924. }
  6925. // Anchor on left side
  6926. }
  6927. else if (anchorX && anchorX < 0) {
  6928. // Chevron
  6929. if (anchorY > y + safeDistance &&
  6930. anchorY < y + h - safeDistance) {
  6931. path.splice(33, 3, 'L', x, anchorY + halfDistance, x - arrowLength, anchorY, x, anchorY - halfDistance, x, y + r);
  6932. // Simple connector
  6933. }
  6934. else {
  6935. path.splice(33, 3, 'L', x, h / 2, anchorX, anchorY, x, h / 2, x, y + r);
  6936. }
  6937. }
  6938. else if ( // replace bottom
  6939. anchorY &&
  6940. anchorY > h &&
  6941. anchorX > x + safeDistance &&
  6942. anchorX < x + w - safeDistance) {
  6943. path.splice(23, 3, 'L', anchorX + halfDistance, y + h, anchorX, y + h + arrowLength, anchorX - halfDistance, y + h, x + r, y + h);
  6944. }
  6945. else if ( // replace top
  6946. anchorY &&
  6947. anchorY < 0 &&
  6948. anchorX > x + safeDistance &&
  6949. anchorX < x + w - safeDistance) {
  6950. path.splice(3, 3, 'L', anchorX - halfDistance, y, anchorX, y - arrowLength, anchorX + halfDistance, y, w - r, y);
  6951. }
  6952. return path;
  6953. }
  6954. },
  6955. /**
  6956. * Define a clipping rectangle. The clipping rectangle is later applied
  6957. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  6958. * function.
  6959. *
  6960. * @example
  6961. * var circle = renderer.circle(100, 100, 100)
  6962. * .attr({ fill: 'red' })
  6963. * .add();
  6964. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  6965. *
  6966. * // Leave only the lower right quarter visible
  6967. * circle.clip(clipRect);
  6968. *
  6969. * @function Highcharts.SVGRenderer#clipRect
  6970. *
  6971. * @param {number} [x]
  6972. *
  6973. * @param {number} [y]
  6974. *
  6975. * @param {number} [width]
  6976. *
  6977. * @param {number} [height]
  6978. *
  6979. * @return {Highcharts.ClipRectElement}
  6980. * A clipping rectangle.
  6981. */
  6982. clipRect: function (x, y, width, height) {
  6983. var wrapper,
  6984. // Add a hyphen at the end to avoid confusion in testing indexes
  6985. // -1 and -10, -11 etc (#6550)
  6986. id = H.uniqueKey() + '-', clipPath = this.createElement('clipPath').attr({
  6987. id: id
  6988. }).add(this.defs);
  6989. wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  6990. wrapper.id = id;
  6991. wrapper.clipPath = clipPath;
  6992. wrapper.count = 0;
  6993. return wrapper;
  6994. },
  6995. /**
  6996. * Draw text. The text can contain a subset of HTML, like spans and anchors
  6997. * and some basic text styling of these. For more advanced features like
  6998. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  6999. * To update the text after render, run `text.attr({ text: 'New text' })`.
  7000. *
  7001. * @sample highcharts/members/renderer-text-on-chart/
  7002. * Annotate the chart freely
  7003. * @sample highcharts/members/renderer-on-chart/
  7004. * Annotate with a border and in response to the data
  7005. * @sample highcharts/members/renderer-text/
  7006. * Formatted text
  7007. *
  7008. * @function Highcharts.SVGRenderer#text
  7009. *
  7010. * @param {string} [str]
  7011. * The text of (subset) HTML to draw.
  7012. *
  7013. * @param {number} [x]
  7014. * The x position of the text's lower left corner.
  7015. *
  7016. * @param {number} [y]
  7017. * The y position of the text's lower left corner.
  7018. *
  7019. * @param {boolean} [useHTML=false]
  7020. * Use HTML to render the text.
  7021. *
  7022. * @return {Highcharts.SVGElement}
  7023. * The text object.
  7024. */
  7025. text: function (str, x, y, useHTML) {
  7026. // declare variables
  7027. var renderer = this, wrapper, attribs = {};
  7028. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  7029. return renderer.html(str, x, y);
  7030. }
  7031. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  7032. if (y) {
  7033. attribs.y = Math.round(y);
  7034. }
  7035. if (defined(str)) {
  7036. attribs.text = str;
  7037. }
  7038. wrapper = renderer.createElement('text')
  7039. .attr(attribs);
  7040. if (!useHTML) {
  7041. wrapper.xSetter = function (value, key, element) {
  7042. var tspans = element.getElementsByTagName('tspan'), tspan, parentVal = element.getAttribute(key), i;
  7043. for (i = 0; i < tspans.length; i++) {
  7044. tspan = tspans[i];
  7045. // If the x values are equal, the tspan represents a
  7046. // linebreak
  7047. if (tspan.getAttribute(key) === parentVal) {
  7048. tspan.setAttribute(key, value);
  7049. }
  7050. }
  7051. element.setAttribute(key, value);
  7052. };
  7053. }
  7054. return wrapper;
  7055. },
  7056. /**
  7057. * Utility to return the baseline offset and total line height from the font
  7058. * size.
  7059. *
  7060. * @function Highcharts.SVGRenderer#fontMetrics
  7061. *
  7062. * @param {number|string} [fontSize]
  7063. * The current font size to inspect. If not given, the font size
  7064. * will be found from the DOM element.
  7065. *
  7066. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [elem]
  7067. * The element to inspect for a current font size.
  7068. *
  7069. * @return {Highcharts.FontMetricsObject}
  7070. * The font metrics.
  7071. */
  7072. fontMetrics: function (fontSize, elem) {
  7073. var lineHeight, baseline;
  7074. if ((this.styledMode || !/px/.test(fontSize)) &&
  7075. win.getComputedStyle // old IE doesn't support it
  7076. ) {
  7077. fontSize = elem && SVGElement.prototype.getStyle.call(elem, 'font-size');
  7078. }
  7079. else {
  7080. fontSize = fontSize ||
  7081. // When the elem is a DOM element (#5932)
  7082. (elem && elem.style && elem.style.fontSize) ||
  7083. // Fall back on the renderer style default
  7084. (this.style && this.style.fontSize);
  7085. }
  7086. // Handle different units
  7087. if (/px/.test(fontSize)) {
  7088. fontSize = pInt(fontSize);
  7089. }
  7090. else {
  7091. fontSize = 12;
  7092. }
  7093. // Empirical values found by comparing font size and bounding box
  7094. // height. Applies to the default font family.
  7095. // https://jsfiddle.net/highcharts/7xvn7/
  7096. lineHeight = fontSize < 24 ? fontSize + 3 : Math.round(fontSize * 1.2);
  7097. baseline = Math.round(lineHeight * 0.8);
  7098. return {
  7099. h: lineHeight,
  7100. b: baseline,
  7101. f: fontSize
  7102. };
  7103. },
  7104. /**
  7105. * Correct X and Y positioning of a label for rotation (#1764).
  7106. *
  7107. * @private
  7108. * @function Highcharts.SVGRenderer#rotCorr
  7109. *
  7110. * @param {number} baseline
  7111. *
  7112. * @param {number} rotation
  7113. *
  7114. * @param {boolean} [alterY]
  7115. *
  7116. * @param {Highcharts.PositionObject}
  7117. */
  7118. rotCorr: function (baseline, rotation, alterY) {
  7119. var y = baseline;
  7120. if (rotation && alterY) {
  7121. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  7122. }
  7123. return {
  7124. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  7125. y: y
  7126. };
  7127. },
  7128. /**
  7129. * Draw a label, which is an extended text element with support for border
  7130. * and background. Highcharts creates a `g` element with a text and a `path`
  7131. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  7132. * background are set through `stroke`, `stroke-width` and `fill` attributes
  7133. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  7134. * text after render, run `label.attr({ text: 'New text' })`.
  7135. *
  7136. * @sample highcharts/members/renderer-label-on-chart/
  7137. * A label on the chart
  7138. *
  7139. * @function Highcharts.SVGRenderer#label
  7140. *
  7141. * @param {string} str
  7142. * The initial text string or (subset) HTML to render.
  7143. *
  7144. * @param {number} x
  7145. * The x position of the label's left side.
  7146. *
  7147. * @param {number} [y]
  7148. * The y position of the label's top side or baseline, depending on
  7149. * the `baseline` parameter.
  7150. *
  7151. * @param {string} [shape='rect']
  7152. * The shape of the label's border/background, if any. Defaults to
  7153. * `rect`. Other possible values are `callout` or other shapes
  7154. * defined in {@link Highcharts.SVGRenderer#symbols}.
  7155. *
  7156. * @param {number} [anchorX]
  7157. * In case the `shape` has a pointer, like a flag, this is the
  7158. * coordinates it should be pinned to.
  7159. *
  7160. * @param {number} [anchorY]
  7161. * In case the `shape` has a pointer, like a flag, this is the
  7162. * coordinates it should be pinned to.
  7163. *
  7164. * @param {boolean} [useHTML=false]
  7165. * Wether to use HTML to render the label.
  7166. *
  7167. * @param {boolean} [baseline=false]
  7168. * Whether to position the label relative to the text baseline,
  7169. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  7170. * upper border of the rectangle.
  7171. *
  7172. * @param {string} [className]
  7173. * Class name for the group.
  7174. *
  7175. * @return {Highcharts.SVGElement}
  7176. * The generated label.
  7177. */
  7178. label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  7179. var renderer = this, styledMode = renderer.styledMode, wrapper = renderer.g((className !== 'button' && 'label')), text = wrapper.text = renderer.text('', 0, 0, useHTML)
  7180. .attr({
  7181. zIndex: 1
  7182. }), box, bBox, alignFactor = 0, padding = 3, paddingLeft = 0, width, height, wrapperX, wrapperY, textAlign, deferredAttr = {}, strokeWidth, baselineOffset, hasBGImage = /^url\((.*?)\)$/.test(shape), needsBox = styledMode || hasBGImage, getCrispAdjust = function () {
  7183. return styledMode ?
  7184. box.strokeWidth() % 2 / 2 :
  7185. (strokeWidth ? parseInt(strokeWidth, 10) : 0) % 2 / 2;
  7186. }, updateBoxSize, updateTextPadding, boxAttr;
  7187. if (className) {
  7188. wrapper.addClass('highcharts-' + className);
  7189. }
  7190. /* This function runs after the label is added to the DOM (when the
  7191. bounding box is available), and after the text of the label is
  7192. updated to detect the new bounding box and reflect it in the border
  7193. box. */
  7194. updateBoxSize = function () {
  7195. var style = text.element.style, crispAdjust, attribs = {};
  7196. bBox = ((typeof width === 'undefined' ||
  7197. typeof height === 'undefined' ||
  7198. textAlign) &&
  7199. defined(text.textStr) &&
  7200. text.getBBox()); // #3295 && 3514 box failure when string equals 0
  7201. wrapper.width = ((width || bBox.width || 0) +
  7202. 2 * padding +
  7203. paddingLeft);
  7204. wrapper.height = (height || bBox.height || 0) + 2 * padding;
  7205. // Update the label-scoped y offset
  7206. baselineOffset = padding + Math.min(renderer
  7207. .fontMetrics(style && style.fontSize, text).b,
  7208. // Math.min because of inline style (#9400)
  7209. bBox ? bBox.height : Infinity);
  7210. if (needsBox) {
  7211. // Create the border box if it is not already present
  7212. if (!box) {
  7213. // Symbol definition exists (#5324)
  7214. wrapper.box = box =
  7215. renderer.symbols[shape] || hasBGImage ?
  7216. renderer.symbol(shape) :
  7217. renderer.rect();
  7218. box.addClass(// Don't use label className for buttons
  7219. (className === 'button' ? '' : 'highcharts-label-box') +
  7220. (className ? ' highcharts-' + className + '-box' : ''));
  7221. box.add(wrapper);
  7222. crispAdjust = getCrispAdjust();
  7223. attribs.x = crispAdjust;
  7224. attribs.y = (baseline ? -baselineOffset : 0) + crispAdjust;
  7225. }
  7226. // Apply the box attributes
  7227. attribs.width = Math.round(wrapper.width);
  7228. attribs.height = Math.round(wrapper.height);
  7229. box.attr(extend(attribs, deferredAttr));
  7230. deferredAttr = {};
  7231. }
  7232. };
  7233. /*
  7234. * This function runs after setting text or padding, but only if padding
  7235. * is changed.
  7236. */
  7237. updateTextPadding = function () {
  7238. var textX = paddingLeft + padding, textY;
  7239. // determin y based on the baseline
  7240. textY = baseline ? 0 : baselineOffset;
  7241. // compensate for alignment
  7242. if (defined(width) &&
  7243. bBox &&
  7244. (textAlign === 'center' || textAlign === 'right')) {
  7245. textX += { center: 0.5, right: 1 }[textAlign] *
  7246. (width - bBox.width);
  7247. }
  7248. // update if anything changed
  7249. if (textX !== text.x || textY !== text.y) {
  7250. text.attr('x', textX);
  7251. // #8159 - prevent misplaced data labels in treemap
  7252. // (useHTML: true)
  7253. if (text.hasBoxWidthChanged) {
  7254. bBox = text.getBBox(true);
  7255. updateBoxSize();
  7256. }
  7257. if (typeof textY !== 'undefined') {
  7258. text.attr('y', textY);
  7259. }
  7260. }
  7261. // record current values
  7262. text.x = textX;
  7263. text.y = textY;
  7264. };
  7265. /*
  7266. * Set a box attribute, or defer it if the box is not yet created
  7267. */
  7268. boxAttr = function (key, value) {
  7269. if (box) {
  7270. box.attr(key, value);
  7271. }
  7272. else {
  7273. deferredAttr[key] = value;
  7274. }
  7275. };
  7276. /*
  7277. * After the text element is added, get the desired size of the border
  7278. * box and add it before the text in the DOM.
  7279. */
  7280. wrapper.onAdd = function () {
  7281. text.add(wrapper);
  7282. wrapper.attr({
  7283. // Alignment is available now (#3295, 0 not rendered if given
  7284. // as a value)
  7285. text: (str || str === 0) ? str : '',
  7286. x: x,
  7287. y: y
  7288. });
  7289. if (box && defined(anchorX)) {
  7290. wrapper.attr({
  7291. anchorX: anchorX,
  7292. anchorY: anchorY
  7293. });
  7294. }
  7295. };
  7296. /*
  7297. * Add specific attribute setters.
  7298. */
  7299. // only change local variables
  7300. wrapper.widthSetter = function (value) {
  7301. // width:auto => null
  7302. width = isNumber(value) ? value : null;
  7303. };
  7304. wrapper.heightSetter = function (value) {
  7305. height = value;
  7306. };
  7307. wrapper['text-alignSetter'] = function (value) {
  7308. textAlign = value;
  7309. };
  7310. wrapper.paddingSetter = function (value) {
  7311. if (defined(value) && value !== padding) {
  7312. padding = wrapper.padding = value;
  7313. updateTextPadding();
  7314. }
  7315. };
  7316. wrapper.paddingLeftSetter = function (value) {
  7317. if (defined(value) && value !== paddingLeft) {
  7318. paddingLeft = value;
  7319. updateTextPadding();
  7320. }
  7321. };
  7322. // change local variable and prevent setting attribute on the group
  7323. wrapper.alignSetter = function (value) {
  7324. value = {
  7325. left: 0,
  7326. center: 0.5,
  7327. right: 1
  7328. }[value];
  7329. if (value !== alignFactor) {
  7330. alignFactor = value;
  7331. // Bounding box exists, means we're dynamically changing
  7332. if (bBox) {
  7333. wrapper.attr({ x: wrapperX }); // #5134
  7334. }
  7335. }
  7336. };
  7337. // apply these to the box and the text alike
  7338. wrapper.textSetter = function (value) {
  7339. if (typeof value !== 'undefined') {
  7340. // Must use .attr to ensure transforms are done (#10009)
  7341. text.attr({
  7342. text: value
  7343. });
  7344. }
  7345. updateBoxSize();
  7346. updateTextPadding();
  7347. };
  7348. // apply these to the box but not to the text
  7349. wrapper['stroke-widthSetter'] = function (value, key) {
  7350. if (value) {
  7351. needsBox = true;
  7352. }
  7353. strokeWidth = this['stroke-width'] = value;
  7354. boxAttr(key, value);
  7355. };
  7356. if (styledMode) {
  7357. wrapper.rSetter = function (value, key) {
  7358. boxAttr(key, value);
  7359. };
  7360. }
  7361. else {
  7362. wrapper.strokeSetter =
  7363. wrapper.fillSetter =
  7364. wrapper.rSetter = function (value, key) {
  7365. if (key !== 'r') {
  7366. if (key === 'fill' && value) {
  7367. needsBox = true;
  7368. }
  7369. // for animation getter (#6776)
  7370. wrapper[key] = value;
  7371. }
  7372. boxAttr(key, value);
  7373. };
  7374. }
  7375. wrapper.anchorXSetter = function (value, key) {
  7376. anchorX = wrapper.anchorX = value;
  7377. boxAttr(key, Math.round(value) - getCrispAdjust() - wrapperX);
  7378. };
  7379. wrapper.anchorYSetter = function (value, key) {
  7380. anchorY = wrapper.anchorY = value;
  7381. boxAttr(key, value - wrapperY);
  7382. };
  7383. // rename attributes
  7384. wrapper.xSetter = function (value) {
  7385. wrapper.x = value; // for animation getter
  7386. if (alignFactor) {
  7387. value -= alignFactor * ((width || bBox.width) + 2 * padding);
  7388. // Force animation even when setting to the same value (#7898)
  7389. wrapper['forceAnimate:x'] = true;
  7390. }
  7391. wrapperX = Math.round(value);
  7392. wrapper.attr('translateX', wrapperX);
  7393. };
  7394. wrapper.ySetter = function (value) {
  7395. wrapperY = wrapper.y = Math.round(value);
  7396. wrapper.attr('translateY', wrapperY);
  7397. };
  7398. // Redirect certain methods to either the box or the text
  7399. var baseCss = wrapper.css;
  7400. var wrapperExtension = {
  7401. /**
  7402. * Pick up some properties and apply them to the text instead of the
  7403. * wrapper.
  7404. */
  7405. css: function (styles) {
  7406. if (styles) {
  7407. var textStyles = {};
  7408. // Create a copy to avoid altering the original object
  7409. // (#537)
  7410. styles = merge(styles);
  7411. wrapper.textProps.forEach(function (prop) {
  7412. if (typeof styles[prop] !== 'undefined') {
  7413. textStyles[prop] = styles[prop];
  7414. delete styles[prop];
  7415. }
  7416. });
  7417. text.css(textStyles);
  7418. // Update existing text and box
  7419. if ('width' in textStyles) {
  7420. updateBoxSize();
  7421. }
  7422. // Keep updated (#9400)
  7423. if ('fontSize' in textStyles) {
  7424. updateBoxSize();
  7425. updateTextPadding();
  7426. }
  7427. }
  7428. return baseCss.call(wrapper, styles);
  7429. },
  7430. /*
  7431. * Return the bounding box of the box, not the group.
  7432. */
  7433. getBBox: function () {
  7434. return {
  7435. width: bBox.width + 2 * padding,
  7436. height: bBox.height + 2 * padding,
  7437. x: bBox.x - padding,
  7438. y: bBox.y - padding
  7439. };
  7440. },
  7441. /**
  7442. * Destroy and release memory.
  7443. */
  7444. destroy: function () {
  7445. // Added by button implementation
  7446. removeEvent(wrapper.element, 'mouseenter');
  7447. removeEvent(wrapper.element, 'mouseleave');
  7448. if (text) {
  7449. text = text.destroy();
  7450. }
  7451. if (box) {
  7452. box = box.destroy();
  7453. }
  7454. // Call base implementation to destroy the rest
  7455. SVGElement.prototype.destroy.call(wrapper);
  7456. // Release local pointers (#1298)
  7457. wrapper =
  7458. renderer =
  7459. updateBoxSize =
  7460. updateTextPadding =
  7461. boxAttr = null;
  7462. }
  7463. };
  7464. if (!styledMode) {
  7465. /**
  7466. * Apply the shadow to the box.
  7467. *
  7468. * @ignore
  7469. * @function Highcharts.SVGElement#shadow
  7470. *
  7471. * @return {Highcharts.SVGElement}
  7472. */
  7473. wrapperExtension.shadow = function (b) {
  7474. if (b) {
  7475. updateBoxSize();
  7476. if (box) {
  7477. box.shadow(b);
  7478. }
  7479. }
  7480. return wrapper;
  7481. };
  7482. }
  7483. return extend(wrapper, wrapperExtension);
  7484. }
  7485. }); // end SVGRenderer
  7486. // general renderer
  7487. H.Renderer = SVGRenderer;
  7488. });
  7489. _registerModule(_modules, 'parts/Html.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  7490. /* *
  7491. *
  7492. * (c) 2010-2019 Torstein Honsi
  7493. *
  7494. * License: www.highcharts.com/license
  7495. *
  7496. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7497. *
  7498. * */
  7499. var attr = U.attr, defined = U.defined, extend = U.extend, pick = U.pick, pInt = U.pInt;
  7500. var createElement = H.createElement, css = H.css, isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, SVGElement = H.SVGElement, SVGRenderer = H.SVGRenderer, win = H.win;
  7501. /* eslint-disable valid-jsdoc */
  7502. // Extend SvgElement for useHTML option.
  7503. extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
  7504. /**
  7505. * Apply CSS to HTML elements. This is used in text within SVG rendering and
  7506. * by the VML renderer
  7507. *
  7508. * @private
  7509. * @function Highcharts.SVGElement#htmlCss
  7510. *
  7511. * @param {Highcharts.CSSObject} styles
  7512. *
  7513. * @return {Highcharts.SVGElement}
  7514. */
  7515. htmlCss: function (styles) {
  7516. var wrapper = this, element = wrapper.element,
  7517. // When setting or unsetting the width style, we need to update
  7518. // transform (#8809)
  7519. isSettingWidth = (element.tagName === 'SPAN' &&
  7520. styles &&
  7521. 'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0), doTransform;
  7522. if (isSettingWidth) {
  7523. delete styles.width;
  7524. wrapper.textWidth = textWidth;
  7525. doTransform = true;
  7526. }
  7527. if (styles && styles.textOverflow === 'ellipsis') {
  7528. styles.whiteSpace = 'nowrap';
  7529. styles.overflow = 'hidden';
  7530. }
  7531. wrapper.styles = extend(wrapper.styles, styles);
  7532. css(wrapper.element, styles);
  7533. // Now that all styles are applied, to the transform
  7534. if (doTransform) {
  7535. wrapper.htmlUpdateTransform();
  7536. }
  7537. return wrapper;
  7538. },
  7539. /**
  7540. * VML and useHTML method for calculating the bounding box based on offsets.
  7541. *
  7542. * @private
  7543. * @function Highcharts.SVGElement#htmlGetBBox
  7544. *
  7545. * @param {boolean} refresh
  7546. * Whether to force a fresh value from the DOM or to use the cached
  7547. * value.
  7548. *
  7549. * @return {Highcharts.BBoxObject}
  7550. * A hash containing values for x, y, width and height.
  7551. */
  7552. htmlGetBBox: function () {
  7553. var wrapper = this, element = wrapper.element;
  7554. return {
  7555. x: element.offsetLeft,
  7556. y: element.offsetTop,
  7557. width: element.offsetWidth,
  7558. height: element.offsetHeight
  7559. };
  7560. },
  7561. /**
  7562. * VML override private method to update elements based on internal
  7563. * properties based on SVG transform.
  7564. *
  7565. * @private
  7566. * @function Highcharts.SVGElement#htmlUpdateTransform
  7567. * @return {void}
  7568. */
  7569. htmlUpdateTransform: function () {
  7570. // aligning non added elements is expensive
  7571. if (!this.added) {
  7572. this.alignOnAdd = true;
  7573. return;
  7574. }
  7575. 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 = {
  7576. left: 0, center: 0.5, right: 1
  7577. }[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
  7578. /**
  7579. * @private
  7580. * @return {number}
  7581. */
  7582. function getTextPxLength() {
  7583. // Reset multiline/ellipsis in order to read width (#4928,
  7584. // #5417)
  7585. css(elem, {
  7586. width: '',
  7587. whiteSpace: whiteSpace || 'nowrap'
  7588. });
  7589. return elem.offsetWidth;
  7590. }
  7591. // apply translate
  7592. css(elem, {
  7593. marginLeft: translateX,
  7594. marginTop: translateY
  7595. });
  7596. if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
  7597. wrapper.shadows.forEach(function (shadow) {
  7598. css(shadow, {
  7599. marginLeft: translateX + 1,
  7600. marginTop: translateY + 1
  7601. });
  7602. });
  7603. }
  7604. // apply inversion
  7605. if (wrapper.inverted) { // wrapper is a group
  7606. [].forEach.call(elem.childNodes, function (child) {
  7607. renderer.invertChild(child, elem);
  7608. });
  7609. }
  7610. if (elem.tagName === 'SPAN') {
  7611. var rotation = wrapper.rotation, baseline, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
  7612. rotation,
  7613. align,
  7614. elem.innerHTML,
  7615. wrapper.textWidth,
  7616. wrapper.textAlign
  7617. ].join(',');
  7618. // Update textWidth. Use the memoized textPxLength if possible, to
  7619. // avoid the getTextPxLength function using elem.offsetWidth.
  7620. // Calling offsetWidth affects rendering time as it forces layout
  7621. // (#7656).
  7622. if (textWidth !== wrapper.oldTextWidth &&
  7623. ((textWidth > wrapper.oldTextWidth) ||
  7624. (wrapper.textPxLength || getTextPxLength()) > textWidth) && (
  7625. // Only set the width if the text is able to word-wrap, or
  7626. // text-overflow is ellipsis (#9537)
  7627. /[ \-]/.test(elem.textContent || elem.innerText) ||
  7628. elem.style.textOverflow === 'ellipsis')) { // #983, #1254
  7629. css(elem, {
  7630. width: textWidth + 'px',
  7631. display: 'block',
  7632. whiteSpace: whiteSpace || 'normal' // #3331
  7633. });
  7634. wrapper.oldTextWidth = textWidth;
  7635. wrapper.hasBoxWidthChanged = true; // #8159
  7636. }
  7637. else {
  7638. wrapper.hasBoxWidthChanged = false; // #8159
  7639. }
  7640. // Do the calculations and DOM access only if properties changed
  7641. if (currentTextTransform !== wrapper.cTT) {
  7642. baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
  7643. // Renderer specific handling of span rotation, but only if we
  7644. // have something to update.
  7645. if (defined(rotation) &&
  7646. ((rotation !== (wrapper.oldRotation || 0)) ||
  7647. (align !== wrapper.oldAlign))) {
  7648. wrapper.setSpanRotation(rotation, alignCorrection, baseline);
  7649. }
  7650. wrapper.getSpanCorrection(
  7651. // Avoid elem.offsetWidth if we can, it affects rendering
  7652. // time heavily (#7656)
  7653. ((!defined(rotation) && wrapper.textPxLength) || // #7920
  7654. elem.offsetWidth), baseline, alignCorrection, rotation, align);
  7655. }
  7656. // apply position with correction
  7657. css(elem, {
  7658. left: (x + (wrapper.xCorr || 0)) + 'px',
  7659. top: (y + (wrapper.yCorr || 0)) + 'px'
  7660. });
  7661. // record current text transform
  7662. wrapper.cTT = currentTextTransform;
  7663. wrapper.oldRotation = rotation;
  7664. wrapper.oldAlign = align;
  7665. }
  7666. },
  7667. /**
  7668. * Set the rotation of an individual HTML span.
  7669. *
  7670. * @private
  7671. * @function Highcharts.SVGElement#setSpanRotation
  7672. * @param {number} rotation
  7673. * @param {number} alignCorrection
  7674. * @param {number} baseline
  7675. * @return {void}
  7676. */
  7677. setSpanRotation: function (rotation, alignCorrection, baseline) {
  7678. var rotationStyle = {}, cssTransformKey = this.renderer.getTransformKey();
  7679. rotationStyle[cssTransformKey] = rotationStyle.transform =
  7680. 'rotate(' + rotation + 'deg)';
  7681. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
  7682. rotationStyle.transformOrigin =
  7683. (alignCorrection * 100) + '% ' + baseline + 'px';
  7684. css(this.element, rotationStyle);
  7685. },
  7686. /**
  7687. * Get the correction in X and Y positioning as the element is rotated.
  7688. *
  7689. * @private
  7690. * @function Highcharts.SVGElement#getSpanCorrection
  7691. * @param {number} width
  7692. * @param {number} baseline
  7693. * @param {number} alignCorrection
  7694. * @return {void}
  7695. */
  7696. getSpanCorrection: function (width, baseline, alignCorrection) {
  7697. this.xCorr = -width * alignCorrection;
  7698. this.yCorr = -baseline;
  7699. }
  7700. });
  7701. // Extend SvgRenderer for useHTML option.
  7702. extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
  7703. /**
  7704. * @private
  7705. * @function Highcharts.SVGRenderer#getTransformKey
  7706. *
  7707. * @return {string}
  7708. */
  7709. getTransformKey: function () {
  7710. return isMS && !/Edge/.test(win.navigator.userAgent) ?
  7711. '-ms-transform' :
  7712. isWebKit ?
  7713. '-webkit-transform' :
  7714. isFirefox ?
  7715. 'MozTransform' :
  7716. win.opera ?
  7717. '-o-transform' :
  7718. '';
  7719. },
  7720. /**
  7721. * Create HTML text node. This is used by the VML renderer as well as the
  7722. * SVG renderer through the useHTML option.
  7723. *
  7724. * @private
  7725. * @function Highcharts.SVGRenderer#html
  7726. *
  7727. * @param {string} str
  7728. * The text of (subset) HTML to draw.
  7729. *
  7730. * @param {number} x
  7731. * The x position of the text's lower left corner.
  7732. *
  7733. * @param {number} y
  7734. * The y position of the text's lower left corner.
  7735. *
  7736. * @return {Highcharts.HTMLDOMElement}
  7737. */
  7738. html: function (str, x, y) {
  7739. var wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, isSVG = renderer.isSVG, addSetters = function (gWrapper, style) {
  7740. // These properties are set as attributes on the SVG group, and
  7741. // as identical CSS properties on the div. (#3542)
  7742. ['opacity', 'visibility'].forEach(function (prop) {
  7743. gWrapper[prop + 'Setter'] = function (value, key, elem) {
  7744. var styleObject = gWrapper.div ?
  7745. gWrapper.div.style :
  7746. style;
  7747. SVGElement.prototype[prop + 'Setter']
  7748. .call(this, value, key, elem);
  7749. if (styleObject) {
  7750. styleObject[key] = value;
  7751. }
  7752. };
  7753. });
  7754. gWrapper.addedSetters = true;
  7755. };
  7756. // Text setter
  7757. wrapper.textSetter = function (value) {
  7758. if (value !== element.innerHTML) {
  7759. delete this.bBox;
  7760. delete this.oldTextWidth;
  7761. }
  7762. this.textStr = value;
  7763. element.innerHTML = pick(value, '');
  7764. wrapper.doTransform = true;
  7765. };
  7766. // Add setters for the element itself (#4938)
  7767. if (isSVG) { // #4938, only for HTML within SVG
  7768. addSetters(wrapper, wrapper.element.style);
  7769. }
  7770. // Various setters which rely on update transform
  7771. wrapper.xSetter =
  7772. wrapper.ySetter =
  7773. wrapper.alignSetter =
  7774. wrapper.rotationSetter =
  7775. function (value, key) {
  7776. if (key === 'align') {
  7777. // Do not overwrite the SVGElement.align method. Same as VML.
  7778. key = 'textAlign';
  7779. }
  7780. wrapper[key] = value;
  7781. wrapper.doTransform = true;
  7782. };
  7783. // Runs at the end of .attr()
  7784. wrapper.afterSetters = function () {
  7785. // Update transform. Do this outside the loop to prevent redundant
  7786. // updating for batch setting of attributes.
  7787. if (this.doTransform) {
  7788. this.htmlUpdateTransform();
  7789. this.doTransform = false;
  7790. }
  7791. };
  7792. // Set the default attributes
  7793. wrapper
  7794. .attr({
  7795. text: str,
  7796. x: Math.round(x),
  7797. y: Math.round(y)
  7798. })
  7799. .css({
  7800. position: 'absolute'
  7801. });
  7802. if (!renderer.styledMode) {
  7803. wrapper.css({
  7804. fontFamily: this.style.fontFamily,
  7805. fontSize: this.style.fontSize
  7806. });
  7807. }
  7808. // Keep the whiteSpace style outside the wrapper.styles collection
  7809. element.style.whiteSpace = 'nowrap';
  7810. // Use the HTML specific .css method
  7811. wrapper.css = wrapper.htmlCss;
  7812. // This is specific for HTML within SVG
  7813. if (isSVG) {
  7814. wrapper.add = function (svgGroupWrapper) {
  7815. var htmlGroup, container = renderer.box.parentNode, parentGroup, parents = [];
  7816. this.parentGroup = svgGroupWrapper;
  7817. // Create a mock group to hold the HTML elements
  7818. if (svgGroupWrapper) {
  7819. htmlGroup = svgGroupWrapper.div;
  7820. if (!htmlGroup) {
  7821. // Read the parent chain into an array and read from top
  7822. // down
  7823. parentGroup = svgGroupWrapper;
  7824. while (parentGroup) {
  7825. parents.push(parentGroup);
  7826. // Move up to the next parent group
  7827. parentGroup = parentGroup.parentGroup;
  7828. }
  7829. // Ensure dynamically updating position when any parent
  7830. // is translated
  7831. parents.reverse().forEach(function (parentGroup) {
  7832. var htmlGroupStyle, cls = attr(parentGroup.element, 'class');
  7833. /**
  7834. * Common translate setter for X and Y on the HTML
  7835. * group. Reverted the fix for #6957 du to
  7836. * positioning problems and offline export (#7254,
  7837. * #7280, #7529)
  7838. * @private
  7839. * @param {*} value
  7840. * @param {string} key
  7841. * @return {void}
  7842. */
  7843. function translateSetter(value, key) {
  7844. parentGroup[key] = value;
  7845. if (key === 'translateX') {
  7846. htmlGroupStyle.left = value + 'px';
  7847. }
  7848. else {
  7849. htmlGroupStyle.top = value + 'px';
  7850. }
  7851. parentGroup.doTransform = true;
  7852. }
  7853. // Create a HTML div and append it to the parent div
  7854. // to emulate the SVG group structure
  7855. htmlGroup =
  7856. parentGroup.div =
  7857. parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
  7858. position: 'absolute',
  7859. left: (parentGroup.translateX || 0) + 'px',
  7860. top: (parentGroup.translateY || 0) + 'px',
  7861. display: parentGroup.display,
  7862. opacity: parentGroup.opacity,
  7863. pointerEvents: (parentGroup.styles &&
  7864. parentGroup.styles.pointerEvents) // #5595
  7865. // the top group is appended to container
  7866. }, htmlGroup || container);
  7867. // Shortcut
  7868. htmlGroupStyle = htmlGroup.style;
  7869. // Set listeners to update the HTML div's position
  7870. // whenever the SVG group position is changed.
  7871. extend(parentGroup, {
  7872. // (#7287) Pass htmlGroup to use
  7873. // the related group
  7874. classSetter: (function (htmlGroup) {
  7875. return function (value) {
  7876. this.element.setAttribute('class', value);
  7877. htmlGroup.className = value;
  7878. };
  7879. }(htmlGroup)),
  7880. on: function () {
  7881. if (parents[0].div) { // #6418
  7882. wrapper.on.apply({ element: parents[0].div }, arguments);
  7883. }
  7884. return parentGroup;
  7885. },
  7886. translateXSetter: translateSetter,
  7887. translateYSetter: translateSetter
  7888. });
  7889. if (!parentGroup.addedSetters) {
  7890. addSetters(parentGroup);
  7891. }
  7892. });
  7893. }
  7894. }
  7895. else {
  7896. htmlGroup = container;
  7897. }
  7898. htmlGroup.appendChild(element);
  7899. // Shared with VML:
  7900. wrapper.added = true;
  7901. if (wrapper.alignOnAdd) {
  7902. wrapper.htmlUpdateTransform();
  7903. }
  7904. return wrapper;
  7905. };
  7906. }
  7907. return wrapper;
  7908. }
  7909. });
  7910. });
  7911. _registerModule(_modules, 'parts/Time.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  7912. /* *
  7913. *
  7914. * (c) 2010-2019 Torstein Honsi
  7915. *
  7916. * License: www.highcharts.com/license
  7917. *
  7918. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7919. *
  7920. * */
  7921. var defined = U.defined, extend = U.extend, isObject = U.isObject, objectEach = U.objectEach, pad = U.pad, pick = U.pick, splat = U.splat;
  7922. /**
  7923. * Normalized interval.
  7924. *
  7925. * @interface Highcharts.TimeNormalizedObject
  7926. */ /**
  7927. * The count.
  7928. *
  7929. * @name Highcharts.TimeNormalizedObject#count
  7930. * @type {number}
  7931. */ /**
  7932. * The interval in axis values (ms).
  7933. *
  7934. * @name Highcharts.TimeNormalizedObject#unitRange
  7935. * @type {number}
  7936. */
  7937. /**
  7938. * Function of an additional date format specifier.
  7939. *
  7940. * @callback Highcharts.TimeFormatCallbackFunction
  7941. *
  7942. * @param {number} timestamp
  7943. * The time to format.
  7944. *
  7945. * @return {string}
  7946. * The formatted portion of the date.
  7947. */
  7948. /**
  7949. * Additonal time tick information.
  7950. *
  7951. * @interface Highcharts.TimeTicksInfoObject
  7952. * @augments Highcharts.TimeNormalizedObject
  7953. */ /**
  7954. * @name Highcharts.TimeTicksInfoObject#higherRanks
  7955. * @type {Array<string>}
  7956. */ /**
  7957. * @name Highcharts.TimeTicksInfoObject#totalRange
  7958. * @type {number}
  7959. */
  7960. /**
  7961. * Time ticks.
  7962. *
  7963. * @interface Highcharts.AxisTickPositionsArray
  7964. */ /**
  7965. * @name Highcharts.AxisTickPositionsArray#info
  7966. * @type {Highcharts.TimeTicksInfoObject}
  7967. */
  7968. var H = Highcharts, merge = H.merge, timeUnits = H.timeUnits, win = H.win;
  7969. /* eslint-disable no-invalid-this, valid-jsdoc */
  7970. /**
  7971. * The Time class. Time settings are applied in general for each page using
  7972. * `Highcharts.setOptions`, or individually for each Chart item through the
  7973. * [time](https://api.highcharts.com/highcharts/time) options set.
  7974. *
  7975. * The Time object is available from {@link Highcharts.Chart#time},
  7976. * which refers to `Highcharts.time` if no individual time settings are
  7977. * applied.
  7978. *
  7979. * @example
  7980. * // Apply time settings globally
  7981. * Highcharts.setOptions({
  7982. * time: {
  7983. * timezone: 'Europe/London'
  7984. * }
  7985. * });
  7986. *
  7987. * // Apply time settings by instance
  7988. * var chart = Highcharts.chart('container', {
  7989. * time: {
  7990. * timezone: 'America/New_York'
  7991. * },
  7992. * series: [{
  7993. * data: [1, 4, 3, 5]
  7994. * }]
  7995. * });
  7996. *
  7997. * // Use the Time object
  7998. * console.log(
  7999. * 'Current time in New York',
  8000. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  8001. * );
  8002. *
  8003. * @class
  8004. * @name Highcharts.Time
  8005. *
  8006. * @param {Highcharts.TimeOptions} options
  8007. * Time options as defined in [chart.options.time](/highcharts/time).
  8008. *
  8009. * @since 6.0.5
  8010. */
  8011. Highcharts.Time = function (options) {
  8012. this.update(options, false);
  8013. };
  8014. Highcharts.Time.prototype = {
  8015. /**
  8016. * Time options that can apply globally or to individual charts. These
  8017. * settings affect how `datetime` axes are laid out, how tooltips are
  8018. * formatted, how series
  8019. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  8020. * the Highstock range selector handles time.
  8021. *
  8022. * The common use case is that all charts in the same Highcharts object
  8023. * share the same time settings, in which case the global settings are set
  8024. * using `setOptions`.
  8025. *
  8026. * ```js
  8027. * // Apply time settings globally
  8028. * Highcharts.setOptions({
  8029. * time: {
  8030. * timezone: 'Europe/London'
  8031. * }
  8032. * });
  8033. * // Apply time settings by instance
  8034. * var chart = Highcharts.chart('container', {
  8035. * time: {
  8036. * timezone: 'America/New_York'
  8037. * },
  8038. * series: [{
  8039. * data: [1, 4, 3, 5]
  8040. * }]
  8041. * });
  8042. *
  8043. * // Use the Time object
  8044. * console.log(
  8045. * 'Current time in New York',
  8046. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  8047. * );
  8048. * ```
  8049. *
  8050. * Since v6.0.5, the time options were moved from the `global` obect to the
  8051. * `time` object, and time options can be set on each individual chart.
  8052. *
  8053. * @sample {highcharts|highstock}
  8054. * highcharts/time/timezone/
  8055. * Set the timezone globally
  8056. * @sample {highcharts}
  8057. * highcharts/time/individual/
  8058. * Set the timezone per chart instance
  8059. * @sample {highstock}
  8060. * stock/time/individual/
  8061. * Set the timezone per chart instance
  8062. *
  8063. * @since 6.0.5
  8064. * @optionparent time
  8065. */
  8066. defaultOptions: {
  8067. /**
  8068. * A custom `Date` class for advanced date handling. For example,
  8069. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  8070. * handle Jalali dates.
  8071. *
  8072. * @type {*}
  8073. * @since 4.0.4
  8074. * @product highcharts highstock gantt
  8075. */
  8076. Date: void 0,
  8077. /**
  8078. * A callback to return the time zone offset for a given datetime. It
  8079. * takes the timestamp in terms of milliseconds since January 1 1970,
  8080. * and returns the timezone offset in minutes. This provides a hook
  8081. * for drawing time based charts in specific time zones using their
  8082. * local DST crossover dates, with the help of external libraries.
  8083. *
  8084. * @see [global.timezoneOffset](#global.timezoneOffset)
  8085. *
  8086. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  8087. * Use moment.js to draw Oslo time regardless of browser locale
  8088. *
  8089. * @since 4.1.0
  8090. * @product highcharts highstock gantt
  8091. */
  8092. getTimezoneOffset: void 0,
  8093. /**
  8094. * Requires [moment.js](http://momentjs.com/). If the timezone option
  8095. * is specified, it creates a default
  8096. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  8097. * up the specified timezone in moment.js. If moment.js is not included,
  8098. * this throws a Highcharts error in the console, but does not crash the
  8099. * chart.
  8100. *
  8101. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  8102. *
  8103. * @sample {highcharts|highstock} highcharts/time/timezone/
  8104. * Europe/Oslo
  8105. *
  8106. * @type {string}
  8107. * @since 5.0.7
  8108. * @product highcharts highstock gantt
  8109. */
  8110. timezone: void 0,
  8111. /**
  8112. * The timezone offset in minutes. Positive values are west, negative
  8113. * values are east of UTC, as in the ECMAScript
  8114. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  8115. * method. Use this to display UTC based data in a predefined time zone.
  8116. *
  8117. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  8118. *
  8119. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  8120. * Timezone offset
  8121. *
  8122. * @since 3.0.8
  8123. * @product highcharts highstock gantt
  8124. */
  8125. timezoneOffset: 0,
  8126. /**
  8127. * Whether to use UTC time for axis scaling, tickmark placement and
  8128. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  8129. * is that the time displays equally regardless of the user agent's
  8130. * time zone settings. Local time can be used when the data is loaded
  8131. * in real time or when correct Daylight Saving Time transitions are
  8132. * required.
  8133. *
  8134. * @sample {highcharts} highcharts/time/useutc-true/
  8135. * True by default
  8136. * @sample {highcharts} highcharts/time/useutc-false/
  8137. * False
  8138. */
  8139. useUTC: true
  8140. },
  8141. /**
  8142. * Update the Time object with current options. It is called internally on
  8143. * initializing Highcharts, after running `Highcharts.setOptions` and on
  8144. * `Chart.update`.
  8145. *
  8146. * @private
  8147. * @function Highcharts.Time#update
  8148. *
  8149. * @param {Highcharts.TimeOptions} options
  8150. *
  8151. * @return {void}
  8152. */
  8153. update: function (options) {
  8154. var useUTC = pick(options && options.useUTC, true), time = this;
  8155. this.options = options = merge(true, this.options || {}, options);
  8156. // Allow using a different Date class
  8157. this.Date = options.Date || win.Date || Date;
  8158. this.useUTC = useUTC;
  8159. this.timezoneOffset = (useUTC && options.timezoneOffset);
  8160. /**
  8161. * Get the time zone offset based on the current timezone information as
  8162. * set in the global options.
  8163. *
  8164. * @function Highcharts.Time#getTimezoneOffset
  8165. *
  8166. * @param {number} timestamp
  8167. * The JavaScript timestamp to inspect.
  8168. *
  8169. * @return {number}
  8170. * The timezone offset in minutes compared to UTC.
  8171. */
  8172. this.getTimezoneOffset = this.timezoneOffsetFunction();
  8173. /*
  8174. * The time object has options allowing for variable time zones, meaning
  8175. * the axis ticks or series data needs to consider this.
  8176. */
  8177. this.variableTimezone = !!(!useUTC ||
  8178. options.getTimezoneOffset ||
  8179. options.timezone);
  8180. // UTC time with timezone handling
  8181. if (this.variableTimezone || this.timezoneOffset) {
  8182. this.get = function (unit, date) {
  8183. var realMs = date.getTime(), ms = realMs - time.getTimezoneOffset(date), ret;
  8184. date.setTime(ms); // Temporary adjust to timezone
  8185. ret = date['getUTC' + unit]();
  8186. date.setTime(realMs); // Reset
  8187. return ret;
  8188. };
  8189. this.set = function (unit, date, value) {
  8190. var ms, offset, newOffset;
  8191. // For lower order time units, just set it directly using local
  8192. // time
  8193. if (unit === 'Milliseconds' ||
  8194. unit === 'Seconds' ||
  8195. // If we're dealting with minutes, we only need to
  8196. // consider timezone if we're in Indian time zones with
  8197. // half-hour offsets (#8768).
  8198. (unit === 'Minutes' &&
  8199. date.getTimezoneOffset() % 60 === 0)) {
  8200. date['set' + unit](value);
  8201. // Higher order time units need to take the time zone into
  8202. // account
  8203. }
  8204. else {
  8205. // Adjust by timezone
  8206. offset = time.getTimezoneOffset(date);
  8207. ms = date.getTime() - offset;
  8208. date.setTime(ms);
  8209. date['setUTC' + unit](value);
  8210. newOffset = time.getTimezoneOffset(date);
  8211. ms = date.getTime() + newOffset;
  8212. date.setTime(ms);
  8213. }
  8214. };
  8215. // UTC time with no timezone handling
  8216. }
  8217. else if (useUTC) {
  8218. this.get = function (unit, date) {
  8219. return date['getUTC' + unit]();
  8220. };
  8221. this.set = function (unit, date, value) {
  8222. return date['setUTC' + unit](value);
  8223. };
  8224. // Local time
  8225. }
  8226. else {
  8227. this.get = function (unit, date) {
  8228. return date['get' + unit]();
  8229. };
  8230. this.set = function (unit, date, value) {
  8231. return date['set' + unit](value);
  8232. };
  8233. }
  8234. },
  8235. /**
  8236. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  8237. * local time or a specific timezone time depending on the current time
  8238. * settings.
  8239. *
  8240. * @function Highcharts.Time#makeTime
  8241. *
  8242. * @param {number} year
  8243. * The year
  8244. *
  8245. * @param {number} month
  8246. * The month. Zero-based, so January is 0.
  8247. *
  8248. * @param {number} [date=1]
  8249. * The day of the month
  8250. *
  8251. * @param {number} [hours=0]
  8252. * The hour of the day, 0-23.
  8253. *
  8254. * @param {number} [minutes=0]
  8255. * The minutes
  8256. *
  8257. * @param {number} [seconds=0]
  8258. * The seconds
  8259. *
  8260. * @return {number}
  8261. * The time in milliseconds since January 1st 1970.
  8262. */
  8263. makeTime: function (year, month, date, hours, minutes, seconds) {
  8264. var d, offset, newOffset;
  8265. if (this.useUTC) {
  8266. d = this.Date.UTC.apply(0, arguments);
  8267. offset = this.getTimezoneOffset(d);
  8268. d += offset;
  8269. newOffset = this.getTimezoneOffset(d);
  8270. if (offset !== newOffset) {
  8271. d += newOffset - offset;
  8272. // A special case for transitioning from summer time to winter time.
  8273. // When the clock is set back, the same time is repeated twice, i.e.
  8274. // 02:30 am is repeated since the clock is set back from 3 am to
  8275. // 2 am. We need to make the same time as local Date does.
  8276. }
  8277. else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  8278. !H.isSafari) {
  8279. d -= 36e5;
  8280. }
  8281. }
  8282. else {
  8283. d = new this.Date(year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0)).getTime();
  8284. }
  8285. return d;
  8286. },
  8287. /**
  8288. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  8289. * default getTimezoneOffset function with that timezone is returned. If
  8290. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  8291. * specified, the function using the `timezoneOffset` option or 0 offset is
  8292. * returned.
  8293. *
  8294. * @private
  8295. * @function Highcharts.Time#timezoneOffsetFunction
  8296. *
  8297. * @return {Function}
  8298. * A getTimezoneOffset function
  8299. */
  8300. timezoneOffsetFunction: function () {
  8301. var time = this, options = this.options, moment = win.moment;
  8302. if (!this.useUTC) {
  8303. return function (timestamp) {
  8304. return new Date(timestamp).getTimezoneOffset() * 60000;
  8305. };
  8306. }
  8307. if (options.timezone) {
  8308. if (!moment) {
  8309. // getTimezoneOffset-function stays undefined because it depends
  8310. // on Moment.js
  8311. H.error(25);
  8312. }
  8313. else {
  8314. return function (timestamp) {
  8315. return -moment.tz(timestamp, options.timezone).utcOffset() * 60000;
  8316. };
  8317. }
  8318. }
  8319. // If not timezone is set, look for the getTimezoneOffset callback
  8320. if (this.useUTC && options.getTimezoneOffset) {
  8321. return function (timestamp) {
  8322. return options.getTimezoneOffset(timestamp) * 60000;
  8323. };
  8324. }
  8325. // Last, use the `timezoneOffset` option if set
  8326. return function () {
  8327. return (time.timezoneOffset || 0) * 60000;
  8328. };
  8329. },
  8330. /**
  8331. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  8332. * into a human readable date string. The available format keys are listed
  8333. * below. Additional formats can be given in the
  8334. * {@link Highcharts.dateFormats} hook.
  8335. *
  8336. * Supported format keys:
  8337. * - `%a`: Short weekday, like 'Mon'
  8338. * - `%A`: Long weekday, like 'Monday'
  8339. * - `%d`: Two digit day of the month, 01 to 31
  8340. * - `%e`: Day of the month, 1 through 31
  8341. * - `%w`: Day of the week, 0 through 6
  8342. * - `%b`: Short month, like 'Jan'
  8343. * - `%B`: Long month, like 'January'
  8344. * - `%m`: Two digit month number, 01 through 12
  8345. * - `%y`: Two digits year, like 09 for 2009
  8346. * - `%Y`: Four digits year, like 2009
  8347. * - `%H`: Two digits hours in 24h format, 00 through 23
  8348. * - `%k`: Hours in 24h format, 0 through 23
  8349. * - `%I`: Two digits hours in 12h format, 00 through 11
  8350. * - `%l`: Hours in 12h format, 1 through 12
  8351. * - `%M`: Two digits minutes, 00 through 59
  8352. * - `%p`: Upper case AM or PM
  8353. * - `%P`: Lower case AM or PM
  8354. * - `%S`: Two digits seconds, 00 through 59
  8355. * - `%L`: Milliseconds (naming from Ruby)
  8356. *
  8357. * @example
  8358. * const time = new Highcharts.Time();
  8359. * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
  8360. * console.log(s); // => 2020-01-01 00:00:00
  8361. *
  8362. * @function Highcharts.Time#dateFormat
  8363. *
  8364. * @param {string} [format]
  8365. * The desired format where various time representations are
  8366. * prefixed with %.
  8367. *
  8368. * @param {number} timestamp
  8369. * The JavaScript timestamp.
  8370. *
  8371. * @param {boolean} [capitalize=false]
  8372. * Upper case first letter in the return.
  8373. *
  8374. * @return {string}
  8375. * The formatted date.
  8376. */
  8377. dateFormat: function (format, timestamp, capitalize) {
  8378. if (!defined(timestamp) || isNaN(timestamp)) {
  8379. return H.defaultOptions.lang.invalidDate || '';
  8380. }
  8381. format = pick(format, '%Y-%m-%d %H:%M:%S');
  8382. var time = this, date = new this.Date(timestamp),
  8383. // get the basic time values
  8384. 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.weekdays, shortWeekdays = lang.shortWeekdays,
  8385. // List all format keys. Custom formats can be added from the
  8386. // outside.
  8387. replacements = extend({
  8388. // Day
  8389. // Short weekday, like 'Mon'
  8390. a: shortWeekdays ?
  8391. shortWeekdays[day] :
  8392. langWeekdays[day].substr(0, 3),
  8393. // Long weekday, like 'Monday'
  8394. A: langWeekdays[day],
  8395. // Two digit day of the month, 01 to 31
  8396. d: pad(dayOfMonth),
  8397. // Day of the month, 1 through 31
  8398. e: pad(dayOfMonth, 2, ' '),
  8399. // Day of the week, 0 through 6
  8400. w: day,
  8401. // Week (none implemented)
  8402. // 'W': weekNumber(),
  8403. // Month
  8404. // Short month, like 'Jan'
  8405. b: lang.shortMonths[month],
  8406. // Long month, like 'January'
  8407. B: lang.months[month],
  8408. // Two digit month number, 01 through 12
  8409. m: pad(month + 1),
  8410. // Month number, 1 through 12 (#8150)
  8411. o: month + 1,
  8412. // Year
  8413. // Two digits year, like 09 for 2009
  8414. y: fullYear.toString().substr(2, 2),
  8415. // Four digits year, like 2009
  8416. Y: fullYear,
  8417. // Time
  8418. // Two digits hours in 24h format, 00 through 23
  8419. H: pad(hours),
  8420. // Hours in 24h format, 0 through 23
  8421. k: hours,
  8422. // Two digits hours in 12h format, 00 through 11
  8423. I: pad((hours % 12) || 12),
  8424. // Hours in 12h format, 1 through 12
  8425. l: (hours % 12) || 12,
  8426. // Two digits minutes, 00 through 59
  8427. M: pad(time.get('Minutes', date)),
  8428. // Upper case AM or PM
  8429. p: hours < 12 ? 'AM' : 'PM',
  8430. // Lower case AM or PM
  8431. P: hours < 12 ? 'am' : 'pm',
  8432. // Two digits seconds, 00 through 59
  8433. S: pad(date.getSeconds()),
  8434. // Milliseconds (naming from Ruby)
  8435. L: pad(Math.floor(timestamp % 1000), 3)
  8436. }, H.dateFormats);
  8437. // Do the replaces
  8438. objectEach(replacements, function (val, key) {
  8439. // Regex would do it in one line, but this is faster
  8440. while (format.indexOf('%' + key) !== -1) {
  8441. format = format.replace('%' + key, typeof val === 'function' ? val.call(time, timestamp) : val);
  8442. }
  8443. });
  8444. // Optionally capitalize the string and return
  8445. return capitalize ?
  8446. (format.substr(0, 1).toUpperCase() +
  8447. format.substr(1)) :
  8448. format;
  8449. },
  8450. /**
  8451. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  8452. * an object.
  8453. * @private
  8454. * @param {string|Array<T>|Highcharts.Dictionary<T>} f - General format description
  8455. * @return {Highcharts.Dictionary<T>} - The object definition
  8456. */
  8457. resolveDTLFormat: function (f) {
  8458. if (!isObject(f, true)) { // check for string or array
  8459. f = splat(f);
  8460. return {
  8461. main: f[0],
  8462. from: f[1],
  8463. to: f[2]
  8464. };
  8465. }
  8466. return f;
  8467. },
  8468. /**
  8469. * Return an array with time positions distributed on round time values
  8470. * right and right after min and max. Used in datetime axes as well as for
  8471. * grouping data on a datetime axis.
  8472. *
  8473. * @function Highcharts.Time#getTimeTicks
  8474. *
  8475. * @param {Highcharts.TimeNormalizedObject} normalizedInterval
  8476. * The interval in axis values (ms) and the count
  8477. *
  8478. * @param {number} [min]
  8479. * The minimum in axis values
  8480. *
  8481. * @param {number} [max]
  8482. * The maximum in axis values
  8483. *
  8484. * @param {number} [startOfWeek=1]
  8485. *
  8486. * @return {Highcharts.AxisTickPositionsArray}
  8487. */
  8488. getTimeTicks: function (normalizedInterval, min, max, startOfWeek) {
  8489. var time = this, Date = time.Date, tickPositions = [], i, higherRanks = {}, minYear, // used in months and years as a basis for Date.UTC()
  8490. // When crossing DST, use the max. Resolves #6278.
  8491. minDate = new Date(min), interval = normalizedInterval.unitRange, count = normalizedInterval.count || 1, variableDayLength, minDay;
  8492. startOfWeek = pick(startOfWeek, 1);
  8493. if (defined(min)) { // #1300
  8494. time.set('Milliseconds', minDate, interval >= timeUnits.second ?
  8495. 0 : // #3935
  8496. count * Math.floor(time.get('Milliseconds', minDate) / count)); // #3652, #3654
  8497. if (interval >= timeUnits.second) { // second
  8498. time.set('Seconds', minDate, interval >= timeUnits.minute ?
  8499. 0 : // #3935
  8500. count * Math.floor(time.get('Seconds', minDate) / count));
  8501. }
  8502. if (interval >= timeUnits.minute) { // minute
  8503. time.set('Minutes', minDate, interval >= timeUnits.hour ?
  8504. 0 :
  8505. count * Math.floor(time.get('Minutes', minDate) / count));
  8506. }
  8507. if (interval >= timeUnits.hour) { // hour
  8508. time.set('Hours', minDate, interval >= timeUnits.day ?
  8509. 0 :
  8510. count * Math.floor(time.get('Hours', minDate) / count));
  8511. }
  8512. if (interval >= timeUnits.day) { // day
  8513. time.set('Date', minDate, interval >= timeUnits.month ?
  8514. 1 :
  8515. Math.max(1, count * Math.floor(time.get('Date', minDate) / count)));
  8516. }
  8517. if (interval >= timeUnits.month) { // month
  8518. time.set('Month', minDate, interval >= timeUnits.year ? 0 :
  8519. count * Math.floor(time.get('Month', minDate) / count));
  8520. minYear = time.get('FullYear', minDate);
  8521. }
  8522. if (interval >= timeUnits.year) { // year
  8523. minYear -= minYear % count;
  8524. time.set('FullYear', minDate, minYear);
  8525. }
  8526. // week is a special case that runs outside the hierarchy
  8527. if (interval === timeUnits.week) {
  8528. // get start of current week, independent of count
  8529. minDay = time.get('Day', minDate);
  8530. time.set('Date', minDate, (time.get('Date', minDate) -
  8531. minDay + startOfWeek +
  8532. // We don't want to skip days that are before
  8533. // startOfWeek (#7051)
  8534. (minDay < startOfWeek ? -7 : 0)));
  8535. }
  8536. // Get basics for variable time spans
  8537. minYear = time.get('FullYear', minDate);
  8538. var minMonth = time.get('Month', minDate), minDateDate = time.get('Date', minDate), minHours = time.get('Hours', minDate);
  8539. // Redefine min to the floored/rounded minimum time (#7432)
  8540. min = minDate.getTime();
  8541. // Handle local timezone offset
  8542. if (time.variableTimezone) {
  8543. // Detect whether we need to take the DST crossover into
  8544. // consideration. If we're crossing over DST, the day length may
  8545. // be 23h or 25h and we need to compute the exact clock time for
  8546. // each tick instead of just adding hours. This comes at a cost,
  8547. // so first we find out if it is needed (#4951).
  8548. variableDayLength = (
  8549. // Long range, assume we're crossing over.
  8550. max - min > 4 * timeUnits.month ||
  8551. // Short range, check if min and max are in different time
  8552. // zones.
  8553. time.getTimezoneOffset(min) !==
  8554. time.getTimezoneOffset(max));
  8555. }
  8556. // Iterate and add tick positions at appropriate values
  8557. var t = minDate.getTime();
  8558. i = 1;
  8559. while (t < max) {
  8560. tickPositions.push(t);
  8561. // if the interval is years, use Date.UTC to increase years
  8562. if (interval === timeUnits.year) {
  8563. t = time.makeTime(minYear + i * count, 0);
  8564. // if the interval is months, use Date.UTC to increase months
  8565. }
  8566. else if (interval === timeUnits.month) {
  8567. t = time.makeTime(minYear, minMonth + i * count);
  8568. // if we're using global time, the interval is not fixed as it
  8569. // jumps one hour at the DST crossover
  8570. }
  8571. else if (variableDayLength &&
  8572. (interval === timeUnits.day || interval === timeUnits.week)) {
  8573. t = time.makeTime(minYear, minMonth, minDateDate +
  8574. i * count * (interval === timeUnits.day ? 1 : 7));
  8575. }
  8576. else if (variableDayLength &&
  8577. interval === timeUnits.hour &&
  8578. count > 1) {
  8579. // make sure higher ranks are preserved across DST (#6797,
  8580. // #7621)
  8581. t = time.makeTime(minYear, minMonth, minDateDate, minHours + i * count);
  8582. // else, the interval is fixed and we use simple addition
  8583. }
  8584. else {
  8585. t += interval * count;
  8586. }
  8587. i++;
  8588. }
  8589. // push the last time
  8590. tickPositions.push(t);
  8591. // Handle higher ranks. Mark new days if the time is on midnight
  8592. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  8593. // to prevent looping over dense data grouping (#6156).
  8594. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  8595. tickPositions.forEach(function (t) {
  8596. if (
  8597. // Speed optimization, no need to run dateFormat unless
  8598. // we're on a full or half hour
  8599. t % 1800000 === 0 &&
  8600. // Check for local or global midnight
  8601. time.dateFormat('%H%M%S%L', t) === '000000000') {
  8602. higherRanks[t] = 'day';
  8603. }
  8604. });
  8605. }
  8606. }
  8607. // record information on the chosen unit - for dynamic label formatter
  8608. tickPositions.info = extend(normalizedInterval, {
  8609. higherRanks: higherRanks,
  8610. totalRange: interval * count
  8611. });
  8612. return tickPositions;
  8613. }
  8614. }; // end of Time
  8615. });
  8616. _registerModule(_modules, 'parts/Options.js', [_modules['parts/Globals.js']], function (H) {
  8617. /* *
  8618. *
  8619. * (c) 2010-2019 Torstein Honsi
  8620. *
  8621. * License: www.highcharts.com/license
  8622. *
  8623. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8624. *
  8625. * */
  8626. /**
  8627. * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
  8628. */
  8629. /**
  8630. * Gets fired when a series is added to the chart after load time, using the
  8631. * `addSeries` method. Returning `false` prevents the series from being added.
  8632. *
  8633. * @callback Highcharts.ChartAddSeriesCallbackFunction
  8634. *
  8635. * @param {Highcharts.Chart} this
  8636. * The chart on which the event occured.
  8637. *
  8638. * @param {Highcharts.ChartAddSeriesEventObject} event
  8639. * The event that occured.
  8640. *
  8641. * @return {void}
  8642. */
  8643. /**
  8644. * Contains common event information. Through the `options` property you can
  8645. * access the series options that were passed to the `addSeries` method.
  8646. *
  8647. * @interface Highcharts.ChartAddSeriesEventObject
  8648. */ /**
  8649. * The series options that were passed to the `addSeries` method.
  8650. * @name Highcharts.ChartAddSeriesEventObject#options
  8651. * @type {Highcharts.SeriesOptionsType}
  8652. */ /**
  8653. * Prevents the default behaviour of the event.
  8654. * @name Highcharts.ChartAddSeriesEventObject#preventDefault
  8655. * @type {Function}
  8656. */ /**
  8657. * The event target.
  8658. * @name Highcharts.ChartAddSeriesEventObject#target
  8659. * @type {Highcharts.Chart}
  8660. */ /**
  8661. * The event type.
  8662. * @name Highcharts.ChartAddSeriesEventObject#type
  8663. * @type {"addSeries"}
  8664. */
  8665. /**
  8666. * Gets fired when clicking on the plot background.
  8667. *
  8668. * @callback Highcharts.ChartClickCallbackFunction
  8669. *
  8670. * @param {Highcharts.Chart} this
  8671. * The chart on which the event occured.
  8672. *
  8673. * @param {Highcharts.PointerEventObject} event
  8674. * The event that occured.
  8675. *
  8676. * @return {void}
  8677. */
  8678. /**
  8679. * Contains an axes of the clicked spot.
  8680. *
  8681. * @interface Highcharts.ChartClickEventAxisObject
  8682. */ /**
  8683. * Axis at the clicked spot.
  8684. * @name Highcharts.ChartClickEventAxisObject#axis
  8685. * @type {Highcharts.Axis}
  8686. */ /**
  8687. * Axis value at the clicked spot.
  8688. * @name Highcharts.ChartClickEventAxisObject#value
  8689. * @type {number}
  8690. */
  8691. /**
  8692. * Contains information about the clicked spot on the chart. Remember the unit
  8693. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  8694. *
  8695. * @interface Highcharts.ChartClickEventObject
  8696. * @extends Highcharts.PointerEventObject
  8697. */ /**
  8698. * Information about the x-axis on the clicked spot.
  8699. * @name Highcharts.ChartClickEventObject#xAxis
  8700. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  8701. */ /**
  8702. * Information about the y-axis on the clicked spot.
  8703. * @name Highcharts.ChartClickEventObject#yAxis
  8704. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  8705. */ /**
  8706. * Information about the z-axis on the clicked spot.
  8707. * @name Highcharts.ChartClickEventObject#zAxis
  8708. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  8709. */
  8710. /**
  8711. * Gets fired when the chart is finished loading.
  8712. *
  8713. * @callback Highcharts.ChartLoadCallbackFunction
  8714. *
  8715. * @param {Highcharts.Chart} this
  8716. * The chart on which the event occured.
  8717. *
  8718. * @param {global.Event} event
  8719. * The event that occured.
  8720. *
  8721. * @return {void}
  8722. */
  8723. /**
  8724. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  8725. * after an axis, series or point is modified with the `redraw` option set to
  8726. * `true`.
  8727. *
  8728. * @callback Highcharts.ChartRedrawCallbackFunction
  8729. *
  8730. * @param {Highcharts.Chart} this
  8731. * The chart on which the event occured.
  8732. *
  8733. * @param {global.Event} event
  8734. * The event that occured.
  8735. *
  8736. * @return {void}
  8737. */
  8738. /**
  8739. * Gets fired after initial load of the chart (directly after the `load` event),
  8740. * and after each redraw (directly after the `redraw` event).
  8741. *
  8742. * @callback Highcharts.ChartRenderCallbackFunction
  8743. *
  8744. * @param {Highcharts.Chart} this
  8745. * The chart on which the event occured.
  8746. *
  8747. * @param {global.Event} event
  8748. * The event that occured.
  8749. *
  8750. * @return {void}
  8751. */
  8752. /**
  8753. * Gets fired when an area of the chart has been selected. The default action
  8754. * for the selection event is to zoom the chart to the selected area. It can be
  8755. * prevented by calling `event.preventDefault()` or return false.
  8756. *
  8757. * @callback Highcharts.ChartSelectionCallbackFunction
  8758. *
  8759. * @param {Highcharts.Chart} this
  8760. * The chart on which the event occured.
  8761. *
  8762. * @param {global.ChartSelectionContextObject} event
  8763. * Event informations
  8764. *
  8765. * @return {boolean|undefined}
  8766. * Return false to prevent the default action, usually zoom.
  8767. */
  8768. /**
  8769. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  8770. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  8771. *
  8772. * @interface Highcharts.ChartSelectionContextObject
  8773. * @extends global.Event
  8774. */ /**
  8775. * Arrays containing the axes of each dimension and each axis' min and max
  8776. * values.
  8777. * @name Highcharts.ChartSelectionContextObject#xAxis
  8778. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  8779. */ /**
  8780. * Arrays containing the axes of each dimension and each axis' min and max
  8781. * values.
  8782. * @name Highcharts.ChartSelectionContextObject#yAxis
  8783. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  8784. */
  8785. /**
  8786. * Axis context of the selection.
  8787. *
  8788. * @interface Highcharts.ChartSelectionAxisContextObject
  8789. */ /**
  8790. * The selected Axis.
  8791. * @name Highcharts.ChartSelectionAxisContextObject#axis
  8792. * @type {Highcharts.Axis}
  8793. */ /**
  8794. * The maximum axis value, either automatic or set manually.
  8795. * @name Highcharts.ChartSelectionAxisContextObject#max
  8796. * @type {number}
  8797. */ /**
  8798. * The minimum axis value, either automatic or set manually.
  8799. * @name Highcharts.ChartSelectionAxisContextObject#min
  8800. * @type {number}
  8801. */
  8802. var color = H.color, isTouchDevice = H.isTouchDevice, merge = H.merge, svg = H.svg;
  8803. /* ************************************************************************** *
  8804. * Handle the options *
  8805. * ************************************************************************** */
  8806. /**
  8807. * Global default settings.
  8808. *
  8809. * @name Highcharts.defaultOptions
  8810. * @type {Highcharts.Options}
  8811. */ /**
  8812. * @optionparent
  8813. */
  8814. H.defaultOptions = {
  8815. /**
  8816. * An array containing the default colors for the chart's series. When
  8817. * all colors are used, new colors are pulled from the start again.
  8818. *
  8819. * Default colors can also be set on a series or series.type basis,
  8820. * see [column.colors](#plotOptions.column.colors),
  8821. * [pie.colors](#plotOptions.pie.colors).
  8822. *
  8823. * In styled mode, the colors option doesn't exist. Instead, colors
  8824. * are defined in CSS and applied either through series or point class
  8825. * names, or through the [chart.colorCount](#chart.colorCount) option.
  8826. *
  8827. *
  8828. * ### Legacy
  8829. *
  8830. * In Highcharts 3.x, the default colors were:
  8831. * ```js
  8832. * colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce',
  8833. * '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']
  8834. * ```
  8835. *
  8836. * In Highcharts 2.x, the default colors were:
  8837. * ```js
  8838. * colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
  8839. * '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']
  8840. * ```
  8841. *
  8842. * @sample {highcharts} highcharts/chart/colors/
  8843. * Assign a global color theme
  8844. *
  8845. * @type {Array<Highcharts.ColorString>}
  8846. * @default ["#7cb5ec", "#434348", "#90ed7d", "#f7a35c", "#8085e9",
  8847. * "#f15c80", "#e4d354", "#2b908f", "#f45b5b", "#91e8e1"]
  8848. */
  8849. colors: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' '),
  8850. /**
  8851. * Styled mode only. Configuration object for adding SVG definitions for
  8852. * reusable elements. See [gradients, shadows and
  8853. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  8854. * for more information and code examples.
  8855. *
  8856. * @type {*}
  8857. * @since 5.0.0
  8858. * @apioption defs
  8859. */
  8860. /**
  8861. * @ignore-option
  8862. */
  8863. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  8864. /**
  8865. * The language object is global and it can't be set on each chart
  8866. * initialization. Instead, use `Highcharts.setOptions` to set it before any
  8867. * chart is initialized.
  8868. *
  8869. * ```js
  8870. * Highcharts.setOptions({
  8871. * lang: {
  8872. * months: [
  8873. * 'Janvier', 'Février', 'Mars', 'Avril',
  8874. * 'Mai', 'Juin', 'Juillet', 'Août',
  8875. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  8876. * ],
  8877. * weekdays: [
  8878. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  8879. * 'Jeudi', 'Vendredi', 'Samedi'
  8880. * ]
  8881. * }
  8882. * });
  8883. * ```
  8884. */
  8885. lang: {
  8886. /**
  8887. * The loading text that appears when the chart is set into the loading
  8888. * state following a call to `chart.showLoading`.
  8889. */
  8890. loading: 'Loading...',
  8891. /**
  8892. * An array containing the months names. Corresponds to the `%B` format
  8893. * in `Highcharts.dateFormat()`.
  8894. *
  8895. * @type {Array<string>}
  8896. * @default ["January", "February", "March", "April", "May", "June",
  8897. * "July", "August", "September", "October", "November",
  8898. * "December"]
  8899. */
  8900. months: [
  8901. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  8902. 'August', 'September', 'October', 'November', 'December'
  8903. ],
  8904. /**
  8905. * An array containing the months names in abbreviated form. Corresponds
  8906. * to the `%b` format in `Highcharts.dateFormat()`.
  8907. *
  8908. * @type {Array<string>}
  8909. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  8910. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  8911. */
  8912. shortMonths: [
  8913. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  8914. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  8915. ],
  8916. /**
  8917. * An array containing the weekday names.
  8918. *
  8919. * @type {Array<string>}
  8920. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  8921. * "Friday", "Saturday"]
  8922. */
  8923. weekdays: [
  8924. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  8925. 'Thursday', 'Friday', 'Saturday'
  8926. ],
  8927. /**
  8928. * Short week days, starting Sunday. If not specified, Highcharts uses
  8929. * the first three letters of the `lang.weekdays` option.
  8930. *
  8931. * @sample highcharts/lang/shortweekdays/
  8932. * Finnish two-letter abbreviations
  8933. *
  8934. * @type {Array<string>}
  8935. * @since 4.2.4
  8936. * @apioption lang.shortWeekdays
  8937. */
  8938. /**
  8939. * What to show in a date field for invalid dates. Defaults to an empty
  8940. * string.
  8941. *
  8942. * @type {string}
  8943. * @since 4.1.8
  8944. * @product highcharts highstock
  8945. * @apioption lang.invalidDate
  8946. */
  8947. /**
  8948. * The title appearing on hovering the zoom in button. The text itself
  8949. * defaults to "+" and can be changed in the button options.
  8950. *
  8951. * @type {string}
  8952. * @default Zoom in
  8953. * @product highmaps
  8954. * @apioption lang.zoomIn
  8955. */
  8956. /**
  8957. * The title appearing on hovering the zoom out button. The text itself
  8958. * defaults to "-" and can be changed in the button options.
  8959. *
  8960. * @type {string}
  8961. * @default Zoom out
  8962. * @product highmaps
  8963. * @apioption lang.zoomOut
  8964. */
  8965. /**
  8966. * The default decimal point used in the `Highcharts.numberFormat`
  8967. * method unless otherwise specified in the function arguments.
  8968. *
  8969. * @since 1.2.2
  8970. */
  8971. decimalPoint: '.',
  8972. /**
  8973. * [Metric prefixes](http://en.wikipedia.org/wiki/Metric_prefix) used
  8974. * to shorten high numbers in axis labels. Replacing any of the
  8975. * positions with `null` causes the full number to be written. Setting
  8976. * `numericSymbols` to `null` disables shortening altogether.
  8977. *
  8978. * @sample {highcharts} highcharts/lang/numericsymbols/
  8979. * Replacing the symbols with text
  8980. * @sample {highstock} highcharts/lang/numericsymbols/
  8981. * Replacing the symbols with text
  8982. *
  8983. * @type {Array<string>}
  8984. * @default ["k", "M", "G", "T", "P", "E"]
  8985. * @since 2.3.0
  8986. */
  8987. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  8988. /**
  8989. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  8990. * Use 10000 for Japanese, Korean and various Chinese locales, which
  8991. * use symbols for 10^4, 10^8 and 10^12.
  8992. *
  8993. * @sample highcharts/lang/numericsymbolmagnitude/
  8994. * 10000 magnitude for Japanese
  8995. *
  8996. * @type {number}
  8997. * @default 1000
  8998. * @since 5.0.3
  8999. * @apioption lang.numericSymbolMagnitude
  9000. */
  9001. /**
  9002. * The text for the label appearing when a chart is zoomed.
  9003. *
  9004. * @since 1.2.4
  9005. */
  9006. resetZoom: 'Reset zoom',
  9007. /**
  9008. * The tooltip title for the label appearing when a chart is zoomed.
  9009. *
  9010. * @since 1.2.4
  9011. */
  9012. resetZoomTitle: 'Reset zoom level 1:1',
  9013. /**
  9014. * The default thousands separator used in the `Highcharts.numberFormat`
  9015. * method unless otherwise specified in the function arguments. Defaults
  9016. * to a single space character, which is recommended in
  9017. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  9018. * across Anglo-American and continental European languages.
  9019. *
  9020. * @default \u0020
  9021. * @since 1.2.2
  9022. */
  9023. thousandsSep: ' '
  9024. },
  9025. /**
  9026. * Global options that don't apply to each chart. These options, like
  9027. * the `lang` options, must be set using the `Highcharts.setOptions`
  9028. * method.
  9029. *
  9030. * ```js
  9031. * Highcharts.setOptions({
  9032. * global: {
  9033. * useUTC: false
  9034. * }
  9035. * });
  9036. * ```
  9037. */
  9038. /**
  9039. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  9040. * Use the [libURL](#exporting.libURL) option to configure exporting._
  9041. *
  9042. * The URL to the additional file to lazy load for Android 2.x devices.
  9043. * These devices don't support SVG, so we download a helper file that
  9044. * contains [canvg](http://code.google.com/p/canvg/), its dependency
  9045. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  9046. * our site, you can install canvas-tools.js on your own server and
  9047. * change this option accordingly.
  9048. *
  9049. * @deprecated
  9050. *
  9051. * @type {string}
  9052. * @default http://code.highcharts.com/{version}/modules/canvas-tools.js
  9053. * @product highcharts highmaps
  9054. * @apioption global.canvasToolsURL
  9055. */
  9056. /**
  9057. * This option is deprecated since v6.0.5. Instead, use
  9058. * [time.useUTC](#time.useUTC) that supports individual time settings
  9059. * per chart.
  9060. *
  9061. * @deprecated
  9062. *
  9063. * @type {boolean}
  9064. * @apioption global.useUTC
  9065. */
  9066. /**
  9067. * This option is deprecated since v6.0.5. Instead, use
  9068. * [time.Date](#time.Date) that supports individual time settings
  9069. * per chart.
  9070. *
  9071. * @deprecated
  9072. *
  9073. * @type {Function}
  9074. * @product highcharts highstock
  9075. * @apioption global.Date
  9076. */
  9077. /**
  9078. * This option is deprecated since v6.0.5. Instead, use
  9079. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  9080. * individual time settings per chart.
  9081. *
  9082. * @deprecated
  9083. *
  9084. * @type {Function}
  9085. * @product highcharts highstock
  9086. * @apioption global.getTimezoneOffset
  9087. */
  9088. /**
  9089. * This option is deprecated since v6.0.5. Instead, use
  9090. * [time.timezone](#time.timezone) that supports individual time
  9091. * settings per chart.
  9092. *
  9093. * @deprecated
  9094. *
  9095. * @type {string}
  9096. * @product highcharts highstock
  9097. * @apioption global.timezone
  9098. */
  9099. /**
  9100. * This option is deprecated since v6.0.5. Instead, use
  9101. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  9102. * time settings per chart.
  9103. *
  9104. * @deprecated
  9105. *
  9106. * @type {number}
  9107. * @product highcharts highstock
  9108. * @apioption global.timezoneOffset
  9109. */
  9110. global: {},
  9111. time: H.Time.prototype.defaultOptions,
  9112. /**
  9113. * General options for the chart.
  9114. */
  9115. chart: {
  9116. /**
  9117. * Default `mapData` for all series. If set to a string, it functions
  9118. * as an index into the `Highcharts.maps` array. Otherwise it is
  9119. * interpreted as map data.
  9120. *
  9121. * @see [mapData](#series.map.mapData)
  9122. *
  9123. * @sample maps/demo/geojson
  9124. * Loading geoJSON data
  9125. * @sample maps/chart/topojson
  9126. * Loading topoJSON converted to geoJSON
  9127. *
  9128. * @type {string|Array<*>}
  9129. * @since 5.0.0
  9130. * @product highmaps
  9131. * @apioption chart.map
  9132. */
  9133. /**
  9134. * Set lat/lon transformation definitions for the chart. If not defined,
  9135. * these are extracted from the map data.
  9136. *
  9137. * @type {*}
  9138. * @since 5.0.0
  9139. * @product highmaps
  9140. * @apioption chart.mapTransforms
  9141. */
  9142. /**
  9143. * When using multiple axis, the ticks of two or more opposite axes
  9144. * will automatically be aligned by adding ticks to the axis or axes
  9145. * with the least ticks, as if `tickAmount` were specified.
  9146. *
  9147. * This can be prevented by setting `alignTicks` to false. If the grid
  9148. * lines look messy, it's a good idea to hide them for the secondary
  9149. * axis by setting `gridLineWidth` to 0.
  9150. *
  9151. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  9152. * then the `alignTicks ` will be disabled for the Axis.
  9153. *
  9154. * Disabled for logarithmic axes.
  9155. *
  9156. * @sample {highcharts} highcharts/chart/alignticks-true/
  9157. * True by default
  9158. * @sample {highcharts} highcharts/chart/alignticks-false/
  9159. * False
  9160. * @sample {highstock} stock/chart/alignticks-true/
  9161. * True by default
  9162. * @sample {highstock} stock/chart/alignticks-false/
  9163. * False
  9164. *
  9165. * @type {boolean}
  9166. * @default true
  9167. * @product highcharts highstock gantt
  9168. * @apioption chart.alignTicks
  9169. */
  9170. /**
  9171. * Set the overall animation for all chart updating. Animation can be
  9172. * disabled throughout the chart by setting it to false here. It can
  9173. * be overridden for each individual API method as a function parameter.
  9174. * The only animation not affected by this option is the initial series
  9175. * animation, see [plotOptions.series.animation](
  9176. * #plotOptions.series.animation).
  9177. *
  9178. * The animation can either be set as a boolean or a configuration
  9179. * object. If `true`, it will use the 'swing' jQuery easing and a
  9180. * duration of 500 ms. If used as a configuration object, the following
  9181. * properties are supported:
  9182. *
  9183. * - **duration**: The duration of the animation in milliseconds.
  9184. *
  9185. * - **easing**: A string reference to an easing function set on the
  9186. * `Math` object. See
  9187. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  9188. *
  9189. * @sample {highcharts} highcharts/chart/animation-none/
  9190. * Updating with no animation
  9191. * @sample {highcharts} highcharts/chart/animation-duration/
  9192. * With a longer duration
  9193. * @sample {highcharts} highcharts/chart/animation-easing/
  9194. * With a jQuery UI easing
  9195. * @sample {highmaps} maps/chart/animation-none/
  9196. * Updating with no animation
  9197. * @sample {highmaps} maps/chart/animation-duration/
  9198. * With a longer duration
  9199. *
  9200. * @type {boolean|Highcharts.AnimationOptionsObject}
  9201. * @default true
  9202. * @apioption chart.animation
  9203. */
  9204. /**
  9205. * A CSS class name to apply to the charts container `div`, allowing
  9206. * unique CSS styling for each chart.
  9207. *
  9208. * @type {string}
  9209. * @apioption chart.className
  9210. */
  9211. /**
  9212. * Event listeners for the chart.
  9213. *
  9214. * @apioption chart.events
  9215. */
  9216. /**
  9217. * Fires when a series is added to the chart after load time, using the
  9218. * `addSeries` method. One parameter, `event`, is passed to the
  9219. * function, containing common event information. Through
  9220. * `event.options` you can access the series options that were passed to
  9221. * the `addSeries` method. Returning false prevents the series from
  9222. * being added.
  9223. *
  9224. * @sample {highcharts} highcharts/chart/events-addseries/
  9225. * Alert on add series
  9226. * @sample {highstock} stock/chart/events-addseries/
  9227. * Alert on add series
  9228. *
  9229. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  9230. * @since 1.2.0
  9231. * @context Highcharts.Chart
  9232. * @apioption chart.events.addSeries
  9233. */
  9234. /**
  9235. * Fires when clicking on the plot background. One parameter, `event`,
  9236. * is passed to the function, containing common event information.
  9237. *
  9238. * Information on the clicked spot can be found through `event.xAxis`
  9239. * and `event.yAxis`, which are arrays containing the axes of each
  9240. * dimension and each axis' value at the clicked spot. The primary axes
  9241. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  9242. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  9243. *
  9244. * ```js
  9245. * click: function(e) {
  9246. * console.log(
  9247. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  9248. * e.yAxis[0].value
  9249. * )
  9250. * }
  9251. * ```
  9252. *
  9253. * @sample {highcharts} highcharts/chart/events-click/
  9254. * Alert coordinates on click
  9255. * @sample {highcharts} highcharts/chart/events-container/
  9256. * Alternatively, attach event to container
  9257. * @sample {highstock} stock/chart/events-click/
  9258. * Alert coordinates on click
  9259. * @sample {highstock} highcharts/chart/events-container/
  9260. * Alternatively, attach event to container
  9261. * @sample {highmaps} maps/chart/events-click/
  9262. * Record coordinates on click
  9263. * @sample {highmaps} highcharts/chart/events-container/
  9264. * Alternatively, attach event to container
  9265. *
  9266. * @type {Highcharts.ChartClickCallbackFunction}
  9267. * @since 1.2.0
  9268. * @context Highcharts.Chart
  9269. * @apioption chart.events.click
  9270. */
  9271. /**
  9272. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  9273. * for images to be loaded, for example from point markers. One
  9274. * parameter, `event`, is passed to the function, containing common
  9275. * event information.
  9276. *
  9277. * There is also a second parameter to the chart constructor where a
  9278. * callback function can be passed to be executed on chart.load.
  9279. *
  9280. * @sample {highcharts} highcharts/chart/events-load/
  9281. * Alert on chart load
  9282. * @sample {highstock} stock/chart/events-load/
  9283. * Alert on chart load
  9284. * @sample {highmaps} maps/chart/events-load/
  9285. * Add series on chart load
  9286. *
  9287. * @type {Highcharts.ChartLoadCallbackFunction}
  9288. * @context Highcharts.Chart
  9289. * @apioption chart.events.load
  9290. */
  9291. /**
  9292. * Fires when the chart is redrawn, either after a call to
  9293. * `chart.redraw()` or after an axis, series or point is modified with
  9294. * the `redraw` option set to `true`. One parameter, `event`, is passed
  9295. * to the function, containing common event information.
  9296. *
  9297. * @sample {highcharts} highcharts/chart/events-redraw/
  9298. * Alert on chart redraw
  9299. * @sample {highstock} stock/chart/events-redraw/
  9300. * Alert on chart redraw when adding a series or moving the
  9301. * zoomed range
  9302. * @sample {highmaps} maps/chart/events-redraw/
  9303. * Set subtitle on chart redraw
  9304. *
  9305. * @type {Highcharts.ChartRedrawCallbackFunction}
  9306. * @since 1.2.0
  9307. * @context Highcharts.Chart
  9308. * @apioption chart.events.redraw
  9309. */
  9310. /**
  9311. * Fires after initial load of the chart (directly after the `load`
  9312. * event), and after each redraw (directly after the `redraw` event).
  9313. *
  9314. * @type {Highcharts.ChartRenderCallbackFunction}
  9315. * @since 5.0.7
  9316. * @context Highcharts.Chart
  9317. * @apioption chart.events.render
  9318. */
  9319. /**
  9320. * Fires when an area of the chart has been selected. Selection is
  9321. * enabled by setting the chart's zoomType. One parameter, `event`, is
  9322. * passed to the function, containing common event information. The
  9323. * default action for the selection event is to zoom the chart to the
  9324. * selected area. It can be prevented by calling
  9325. * `event.preventDefault()` or return false.
  9326. *
  9327. * Information on the selected area can be found through `event.xAxis`
  9328. * and `event.yAxis`, which are arrays containing the axes of each
  9329. * dimension and each axis' min and max values. The primary axes are
  9330. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  9331. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  9332. *
  9333. * ```js
  9334. * selection: function(event) {
  9335. * // log the min and max of the primary, datetime x-axis
  9336. * console.log(
  9337. * Highcharts.dateFormat(
  9338. * '%Y-%m-%d %H:%M:%S',
  9339. * event.xAxis[0].min
  9340. * ),
  9341. * Highcharts.dateFormat(
  9342. * '%Y-%m-%d %H:%M:%S',
  9343. * event.xAxis[0].max
  9344. * )
  9345. * );
  9346. * // log the min and max of the y axis
  9347. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  9348. * }
  9349. * ```
  9350. *
  9351. * @sample {highcharts} highcharts/chart/events-selection/
  9352. * Report on selection and reset
  9353. * @sample {highcharts} highcharts/chart/events-selection-points/
  9354. * Select a range of points through a drag selection
  9355. * @sample {highstock} stock/chart/events-selection/
  9356. * Report on selection and reset
  9357. * @sample {highstock} highcharts/chart/events-selection-points/
  9358. * Select a range of points through a drag selection
  9359. * (Highcharts)
  9360. *
  9361. * @type {Highcharts.ChartSelectionCallbackFunction}
  9362. * @apioption chart.events.selection
  9363. */
  9364. /**
  9365. * The margin between the outer edge of the chart and the plot area.
  9366. * The numbers in the array designate top, right, bottom and left
  9367. * respectively. Use the options `marginTop`, `marginRight`,
  9368. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  9369. *
  9370. * By default there is no margin. The actual space is dynamically
  9371. * calculated from the offset of axis labels, axis title, title,
  9372. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  9373. * `spacingBottom` and `spacingLeft` options.
  9374. *
  9375. * @sample {highcharts} highcharts/chart/margins-zero/
  9376. * Zero margins
  9377. * @sample {highstock} stock/chart/margin-zero/
  9378. * Zero margins
  9379. *
  9380. * @type {number|Array<number>}
  9381. * @apioption chart.margin
  9382. */
  9383. /**
  9384. * The margin between the bottom outer edge of the chart and the plot
  9385. * area. Use this to set a fixed pixel value for the margin as opposed
  9386. * to the default dynamic margin. See also `spacingBottom`.
  9387. *
  9388. * @sample {highcharts} highcharts/chart/marginbottom/
  9389. * 100px bottom margin
  9390. * @sample {highstock} stock/chart/marginbottom/
  9391. * 100px bottom margin
  9392. * @sample {highmaps} maps/chart/margin/
  9393. * 100px margins
  9394. *
  9395. * @type {number}
  9396. * @since 2.0
  9397. * @apioption chart.marginBottom
  9398. */
  9399. /**
  9400. * The margin between the left outer edge of the chart and the plot
  9401. * area. Use this to set a fixed pixel value for the margin as opposed
  9402. * to the default dynamic margin. See also `spacingLeft`.
  9403. *
  9404. * @sample {highcharts} highcharts/chart/marginleft/
  9405. * 150px left margin
  9406. * @sample {highstock} stock/chart/marginleft/
  9407. * 150px left margin
  9408. * @sample {highmaps} maps/chart/margin/
  9409. * 100px margins
  9410. *
  9411. * @type {number}
  9412. * @since 2.0
  9413. * @apioption chart.marginLeft
  9414. */
  9415. /**
  9416. * The margin between the right outer edge of the chart and the plot
  9417. * area. Use this to set a fixed pixel value for the margin as opposed
  9418. * to the default dynamic margin. See also `spacingRight`.
  9419. *
  9420. * @sample {highcharts} highcharts/chart/marginright/
  9421. * 100px right margin
  9422. * @sample {highstock} stock/chart/marginright/
  9423. * 100px right margin
  9424. * @sample {highmaps} maps/chart/margin/
  9425. * 100px margins
  9426. *
  9427. * @type {number}
  9428. * @since 2.0
  9429. * @apioption chart.marginRight
  9430. */
  9431. /**
  9432. * The margin between the top outer edge of the chart and the plot area.
  9433. * Use this to set a fixed pixel value for the margin as opposed to
  9434. * the default dynamic margin. See also `spacingTop`.
  9435. *
  9436. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  9437. * @sample {highstock} stock/chart/margintop/
  9438. * 100px top margin
  9439. * @sample {highmaps} maps/chart/margin/
  9440. * 100px margins
  9441. *
  9442. * @type {number}
  9443. * @since 2.0
  9444. * @apioption chart.marginTop
  9445. */
  9446. /**
  9447. * Callback function to override the default function that formats all
  9448. * the numbers in the chart. Returns a string with the formatted number.
  9449. *
  9450. * @sample highcharts/members/highcharts-numberformat
  9451. * Arabic digits in Highcharts
  9452. * @type {Highcharts.NumberFormatterCallbackFunction}
  9453. * @since 8.0.0
  9454. * @apioption chart.numberFormatter
  9455. */
  9456. /**
  9457. * Allows setting a key to switch between zooming and panning. Can be
  9458. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  9459. * key on Windows) or `shift`. The keys are mapped directly to the key
  9460. * properties of the click event argument (`event.altKey`,
  9461. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  9462. *
  9463. * @type {string}
  9464. * @since 4.0.3
  9465. * @product highcharts gantt
  9466. * @validvalue ["alt", "ctrl", "meta", "shift"]
  9467. * @apioption chart.panKey
  9468. */
  9469. /**
  9470. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  9471. * to combine zooming and panning.
  9472. *
  9473. * On touch devices, when the [tooltip.followTouchMove](
  9474. * #tooltip.followTouchMove) option is `true` (default), panning
  9475. * requires two fingers. To allow panning with one finger, set
  9476. * `followTouchMove` to `false`.
  9477. *
  9478. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  9479. * @sample {highstock} stock/chart/panning/ Zooming and xy panning
  9480. *
  9481. * @product highcharts highstock gantt
  9482. * @apioption chart.panning
  9483. */
  9484. /**
  9485. * Enable or disable chart panning.
  9486. *
  9487. * @type {boolean}
  9488. * @default {highcharts} false
  9489. * @default {highstock} true
  9490. * @apioption chart.panning.enabled
  9491. */
  9492. /**
  9493. * Decides in what dimensions the user can pan the chart. Can be
  9494. * one of `x`, `y`, or `xy`.
  9495. *
  9496. * @type {string}
  9497. * @validvalue ["x", "y", "xy"]
  9498. * @default x
  9499. * @apioption chart.panning.type
  9500. */
  9501. /**
  9502. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  9503. * gestures only. By default, the `pinchType` is the same as the
  9504. * `zoomType` setting. However, pinching can be enabled separately in
  9505. * some cases, for example in stock charts where a mouse drag pans the
  9506. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  9507. * #tooltip.followTouchMove) is true, pinchType only applies to
  9508. * two-finger touches.
  9509. *
  9510. * @type {string}
  9511. * @default {highcharts} undefined
  9512. * @default {highstock} x
  9513. * @since 3.0
  9514. * @product highcharts highstock gantt
  9515. * @validvalue ["x", "y", "xy"]
  9516. * @apioption chart.pinchType
  9517. */
  9518. /**
  9519. * Whether to apply styled mode. When in styled mode, no presentational
  9520. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  9521. * are required to style the chart. The default style sheet is
  9522. * available from `https://code.highcharts.com/css/highcharts.css`.
  9523. *
  9524. * @type {boolean}
  9525. * @default false
  9526. * @since 7.0
  9527. * @apioption chart.styledMode
  9528. */
  9529. styledMode: false,
  9530. /**
  9531. * The corner radius of the outer chart border.
  9532. *
  9533. * @sample {highcharts} highcharts/chart/borderradius/
  9534. * 20px radius
  9535. * @sample {highstock} stock/chart/border/
  9536. * 10px radius
  9537. * @sample {highmaps} maps/chart/border/
  9538. * Border options
  9539. *
  9540. */
  9541. borderRadius: 0,
  9542. /**
  9543. * In styled mode, this sets how many colors the class names
  9544. * should rotate between. With ten colors, series (or points) are
  9545. * given class names like `highcharts-color-0`, `highcharts-color-0`
  9546. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  9547. * is to set colors using the [colors](#colors) setting.
  9548. *
  9549. * @since 5.0.0
  9550. */
  9551. colorCount: 10,
  9552. /**
  9553. * Alias of `type`.
  9554. *
  9555. * @sample {highcharts} highcharts/chart/defaultseriestype/
  9556. * Bar
  9557. *
  9558. * @deprecated
  9559. *
  9560. * @product highcharts
  9561. */
  9562. defaultSeriesType: 'line',
  9563. /**
  9564. * If true, the axes will scale to the remaining visible series once
  9565. * one series is hidden. If false, hiding and showing a series will
  9566. * not affect the axes or the other series. For stacks, once one series
  9567. * within the stack is hidden, the rest of the stack will close in
  9568. * around it even if the axis is not affected.
  9569. *
  9570. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  9571. * True by default
  9572. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  9573. * False
  9574. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  9575. * True with stack
  9576. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  9577. * True by default
  9578. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  9579. * False
  9580. *
  9581. * @since 1.2.0
  9582. * @product highcharts highstock gantt
  9583. */
  9584. ignoreHiddenSeries: true,
  9585. /**
  9586. * Whether to invert the axes so that the x axis is vertical and y axis
  9587. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  9588. * by default.
  9589. *
  9590. * @productdesc {highcharts}
  9591. * If a bar series is present in the chart, it will be inverted
  9592. * automatically. Inverting the chart doesn't have an effect if there
  9593. * are no cartesian series in the chart, or if the chart is
  9594. * [polar](#chart.polar).
  9595. *
  9596. * @sample {highcharts} highcharts/chart/inverted/
  9597. * Inverted line
  9598. * @sample {highstock} stock/navigator/inverted/
  9599. * Inverted stock chart
  9600. *
  9601. * @type {boolean}
  9602. * @default false
  9603. * @product highcharts highstock gantt
  9604. * @apioption chart.inverted
  9605. */
  9606. /**
  9607. * The distance between the outer edge of the chart and the content,
  9608. * like title or legend, or axis title and labels if present. The
  9609. * numbers in the array designate top, right, bottom and left
  9610. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  9611. * and spacingLeft options for shorthand setting of one option.
  9612. *
  9613. * @type {Array<number>}
  9614. * @see [chart.margin](#chart.margin)
  9615. * @default [10, 10, 15, 10]
  9616. * @since 3.0.6
  9617. */
  9618. spacing: [10, 10, 15, 10],
  9619. /**
  9620. * The button that appears after a selection zoom, allowing the user
  9621. * to reset zoom.
  9622. */
  9623. resetZoomButton: {
  9624. /**
  9625. * What frame the button placement should be related to. Can be
  9626. * either `plotBox` or `spacingBox`.
  9627. *
  9628. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  9629. * Relative to the chart
  9630. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  9631. * Relative to the chart
  9632. *
  9633. * @type {Highcharts.ButtonRelativeToValue}
  9634. * @default plot
  9635. * @since 2.2
  9636. * @apioption chart.resetZoomButton.relativeTo
  9637. */
  9638. /**
  9639. * A collection of attributes for the button. The object takes SVG
  9640. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  9641. * border radius. The theme also supports `style`, a collection of
  9642. * CSS properties for the text. Equivalent attributes for the hover
  9643. * state are given in `theme.states.hover`.
  9644. *
  9645. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  9646. * Theming the button
  9647. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  9648. * Theming the button
  9649. *
  9650. * @type {Highcharts.SVGAttributes}
  9651. * @since 2.2
  9652. */
  9653. theme: {
  9654. /** @internal */
  9655. zIndex: 6
  9656. },
  9657. /**
  9658. * The position of the button.
  9659. *
  9660. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  9661. * Above the plot area
  9662. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  9663. * Above the plot area
  9664. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  9665. * Above the plot area
  9666. *
  9667. * @type {Highcharts.AlignObject}
  9668. * @since 2.2
  9669. */
  9670. position: {
  9671. /**
  9672. * The horizontal alignment of the button.
  9673. */
  9674. align: 'right',
  9675. /**
  9676. * The horizontal offset of the button.
  9677. */
  9678. x: -10,
  9679. /**
  9680. * The vertical alignment of the button.
  9681. *
  9682. * @type {Highcharts.VerticalAlignValue}
  9683. * @default top
  9684. * @apioption chart.resetZoomButton.position.verticalAlign
  9685. */
  9686. /**
  9687. * The vertical offset of the button.
  9688. */
  9689. y: 10
  9690. }
  9691. },
  9692. /**
  9693. * The pixel width of the plot area border.
  9694. *
  9695. * @sample {highcharts} highcharts/chart/plotborderwidth/
  9696. * 1px border
  9697. * @sample {highstock} stock/chart/plotborder/
  9698. * 2px border
  9699. * @sample {highmaps} maps/chart/plotborder/
  9700. * Plot border options
  9701. *
  9702. * @type {number}
  9703. * @default 0
  9704. * @apioption chart.plotBorderWidth
  9705. */
  9706. /**
  9707. * Whether to apply a drop shadow to the plot area. Requires that
  9708. * plotBackgroundColor be set. The shadow can be an object configuration
  9709. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  9710. *
  9711. * @sample {highcharts} highcharts/chart/plotshadow/
  9712. * Plot shadow
  9713. * @sample {highstock} stock/chart/plotshadow/
  9714. * Plot shadow
  9715. * @sample {highmaps} maps/chart/plotborder/
  9716. * Plot border options
  9717. *
  9718. * @type {boolean|Highcharts.CSSObject}
  9719. * @default false
  9720. * @apioption chart.plotShadow
  9721. */
  9722. /**
  9723. * When true, cartesian charts like line, spline, area and column are
  9724. * transformed into the polar coordinate system. This produces _polar
  9725. * charts_, also known as _radar charts_.
  9726. *
  9727. * @sample {highcharts} highcharts/demo/polar/
  9728. * Polar chart
  9729. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  9730. * Wind rose, stacked polar column chart
  9731. * @sample {highcharts} highcharts/demo/polar-spider/
  9732. * Spider web chart
  9733. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  9734. * Star plot, multivariate data in a polar chart
  9735. *
  9736. * @type {boolean}
  9737. * @default false
  9738. * @since 2.3.0
  9739. * @product highcharts
  9740. * @requires highcharts-more
  9741. * @apioption chart.polar
  9742. */
  9743. /**
  9744. * Whether to reflow the chart to fit the width of the container div
  9745. * on resizing the window.
  9746. *
  9747. * @sample {highcharts} highcharts/chart/reflow-true/
  9748. * True by default
  9749. * @sample {highcharts} highcharts/chart/reflow-false/
  9750. * False
  9751. * @sample {highstock} stock/chart/reflow-true/
  9752. * True by default
  9753. * @sample {highstock} stock/chart/reflow-false/
  9754. * False
  9755. * @sample {highmaps} maps/chart/reflow-true/
  9756. * True by default
  9757. * @sample {highmaps} maps/chart/reflow-false/
  9758. * False
  9759. *
  9760. * @type {boolean}
  9761. * @default true
  9762. * @since 2.1
  9763. * @apioption chart.reflow
  9764. */
  9765. /**
  9766. * The HTML element where the chart will be rendered. If it is a string,
  9767. * the element by that id is used. The HTML element can also be passed
  9768. * by direct reference, or as the first argument of the chart
  9769. * constructor, in which case the option is not needed.
  9770. *
  9771. * @sample {highcharts} highcharts/chart/reflow-true/
  9772. * String
  9773. * @sample {highcharts} highcharts/chart/renderto-object/
  9774. * Object reference
  9775. * @sample {highcharts} highcharts/chart/renderto-jquery/
  9776. * Object reference through jQuery
  9777. * @sample {highstock} stock/chart/renderto-string/
  9778. * String
  9779. * @sample {highstock} stock/chart/renderto-object/
  9780. * Object reference
  9781. * @sample {highstock} stock/chart/renderto-jquery/
  9782. * Object reference through jQuery
  9783. *
  9784. * @type {string|Highcharts.HTMLDOMElement}
  9785. * @apioption chart.renderTo
  9786. */
  9787. /**
  9788. * The background color of the marker square when selecting (zooming
  9789. * in on) an area of the chart.
  9790. *
  9791. * @see In styled mode, the selection marker fill is set with the
  9792. * `.highcharts-selection-marker` class.
  9793. *
  9794. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  9795. * @default rgba(51,92,173,0.25)
  9796. * @since 2.1.7
  9797. * @apioption chart.selectionMarkerFill
  9798. */
  9799. /**
  9800. * Whether to apply a drop shadow to the outer chart area. Requires
  9801. * that backgroundColor be set. The shadow can be an object
  9802. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  9803. * `width`.
  9804. *
  9805. * @sample {highcharts} highcharts/chart/shadow/
  9806. * Shadow
  9807. * @sample {highstock} stock/chart/shadow/
  9808. * Shadow
  9809. * @sample {highmaps} maps/chart/border/
  9810. * Chart border and shadow
  9811. *
  9812. * @type {boolean|Highcharts.CSSObject}
  9813. * @default false
  9814. * @apioption chart.shadow
  9815. */
  9816. /**
  9817. * Whether to show the axes initially. This only applies to empty charts
  9818. * where series are added dynamically, as axes are automatically added
  9819. * to cartesian series.
  9820. *
  9821. * @sample {highcharts} highcharts/chart/showaxes-false/
  9822. * False by default
  9823. * @sample {highcharts} highcharts/chart/showaxes-true/
  9824. * True
  9825. *
  9826. * @type {boolean}
  9827. * @since 1.2.5
  9828. * @product highcharts gantt
  9829. * @apioption chart.showAxes
  9830. */
  9831. /**
  9832. * The space between the bottom edge of the chart and the content (plot
  9833. * area, axis title and labels, title, subtitle or legend in top
  9834. * position).
  9835. *
  9836. * @sample {highcharts} highcharts/chart/spacingbottom/
  9837. * Spacing bottom set to 100
  9838. * @sample {highstock} stock/chart/spacingbottom/
  9839. * Spacing bottom set to 100
  9840. * @sample {highmaps} maps/chart/spacing/
  9841. * Spacing 100 all around
  9842. *
  9843. * @type {number}
  9844. * @default 15
  9845. * @since 2.1
  9846. * @apioption chart.spacingBottom
  9847. */
  9848. /**
  9849. * The space between the left edge of the chart and the content (plot
  9850. * area, axis title and labels, title, subtitle or legend in top
  9851. * position).
  9852. *
  9853. * @sample {highcharts} highcharts/chart/spacingleft/
  9854. * Spacing left set to 100
  9855. * @sample {highstock} stock/chart/spacingleft/
  9856. * Spacing left set to 100
  9857. * @sample {highmaps} maps/chart/spacing/
  9858. * Spacing 100 all around
  9859. *
  9860. * @type {number}
  9861. * @default 10
  9862. * @since 2.1
  9863. * @apioption chart.spacingLeft
  9864. */
  9865. /**
  9866. * The space between the right edge of the chart and the content (plot
  9867. * area, axis title and labels, title, subtitle or legend in top
  9868. * position).
  9869. *
  9870. * @sample {highcharts} highcharts/chart/spacingright-100/
  9871. * Spacing set to 100
  9872. * @sample {highcharts} highcharts/chart/spacingright-legend/
  9873. * Legend in right position with default spacing
  9874. * @sample {highstock} stock/chart/spacingright/
  9875. * Spacing set to 100
  9876. * @sample {highmaps} maps/chart/spacing/
  9877. * Spacing 100 all around
  9878. *
  9879. * @type {number}
  9880. * @default 10
  9881. * @since 2.1
  9882. * @apioption chart.spacingRight
  9883. */
  9884. /**
  9885. * The space between the top edge of the chart and the content (plot
  9886. * area, axis title and labels, title, subtitle or legend in top
  9887. * position).
  9888. *
  9889. * @sample {highcharts} highcharts/chart/spacingtop-100/
  9890. * A top spacing of 100
  9891. * @sample {highcharts} highcharts/chart/spacingtop-10/
  9892. * Floating chart title makes the plot area align to the default
  9893. * spacingTop of 10.
  9894. * @sample {highstock} stock/chart/spacingtop/
  9895. * A top spacing of 100
  9896. * @sample {highmaps} maps/chart/spacing/
  9897. * Spacing 100 all around
  9898. *
  9899. * @type {number}
  9900. * @default 10
  9901. * @since 2.1
  9902. * @apioption chart.spacingTop
  9903. */
  9904. /**
  9905. * Additional CSS styles to apply inline to the container `div`. Note
  9906. * that since the default font styles are applied in the renderer, it
  9907. * is ignorant of the individual chart options and must be set globally.
  9908. *
  9909. * @see In styled mode, general chart styles can be set with the
  9910. * `.highcharts-root` class.
  9911. * @sample {highcharts} highcharts/chart/style-serif-font/
  9912. * Using a serif type font
  9913. * @sample {highcharts} highcharts/css/em/
  9914. * Styled mode with relative font sizes
  9915. * @sample {highstock} stock/chart/style/
  9916. * Using a serif type font
  9917. * @sample {highmaps} maps/chart/style-serif-font/
  9918. * Using a serif type font
  9919. *
  9920. * @type {Highcharts.CSSObject}
  9921. * @default {"fontFamily": "\"Lucida Grande\", \"Lucida Sans Unicode\", Verdana, Arial, Helvetica, sans-serif","fontSize":"12px"}
  9922. * @apioption chart.style
  9923. */
  9924. /**
  9925. * The default series type for the chart. Can be any of the chart types
  9926. * listed under [plotOptions](#plotOptions) and [series](#series) or can
  9927. * be a series provided by an additional module.
  9928. *
  9929. * In TypeScript this option has no effect in sense of typing and
  9930. * instead the `type` option must always be set in the series.
  9931. *
  9932. * @sample {highcharts} highcharts/chart/type-bar/
  9933. * Bar
  9934. * @sample {highstock} stock/chart/type/
  9935. * Areaspline
  9936. * @sample {highmaps} maps/chart/type-mapline/
  9937. * Mapline
  9938. *
  9939. * @type {string}
  9940. * @default {highcharts} line
  9941. * @default {highstock} line
  9942. * @default {highmaps} map
  9943. * @since 2.1.0
  9944. * @apioption chart.type
  9945. */
  9946. /**
  9947. * Decides in what dimensions the user can zoom by dragging the mouse.
  9948. * Can be one of `x`, `y` or `xy`.
  9949. *
  9950. * @see [panKey](#chart.panKey)
  9951. *
  9952. * @sample {highcharts} highcharts/chart/zoomtype-none/
  9953. * None by default
  9954. * @sample {highcharts} highcharts/chart/zoomtype-x/
  9955. * X
  9956. * @sample {highcharts} highcharts/chart/zoomtype-y/
  9957. * Y
  9958. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  9959. * Xy
  9960. * @sample {highstock} stock/demo/basic-line/
  9961. * None by default
  9962. * @sample {highstock} stock/chart/zoomtype-x/
  9963. * X
  9964. * @sample {highstock} stock/chart/zoomtype-y/
  9965. * Y
  9966. * @sample {highstock} stock/chart/zoomtype-xy/
  9967. * Xy
  9968. *
  9969. * @type {string}
  9970. * @product highcharts highstock gantt
  9971. * @validvalue ["x", "y", "xy"]
  9972. * @apioption chart.zoomType
  9973. */
  9974. /**
  9975. * An explicit width for the chart. By default (when `null`) the width
  9976. * is calculated from the offset width of the containing element.
  9977. *
  9978. * @sample {highcharts} highcharts/chart/width/
  9979. * 800px wide
  9980. * @sample {highstock} stock/chart/width/
  9981. * 800px wide
  9982. * @sample {highmaps} maps/chart/size/
  9983. * Chart with explicit size
  9984. *
  9985. * @type {null|number|string}
  9986. */
  9987. width: null,
  9988. /**
  9989. * An explicit height for the chart. If a _number_, the height is
  9990. * given in pixels. If given a _percentage string_ (for example
  9991. * `'56%'`), the height is given as the percentage of the actual chart
  9992. * width. This allows for preserving the aspect ratio across responsive
  9993. * sizes.
  9994. *
  9995. * By default (when `null`) the height is calculated from the offset
  9996. * height of the containing element, or 400 pixels if the containing
  9997. * element's height is 0.
  9998. *
  9999. * @sample {highcharts} highcharts/chart/height/
  10000. * 500px height
  10001. * @sample {highstock} stock/chart/height/
  10002. * 300px height
  10003. * @sample {highmaps} maps/chart/size/
  10004. * Chart with explicit size
  10005. * @sample highcharts/chart/height-percent/
  10006. * Highcharts with percentage height
  10007. *
  10008. * @type {null|number|string}
  10009. */
  10010. height: null,
  10011. /**
  10012. * The color of the outer chart border.
  10013. *
  10014. * @see In styled mode, the stroke is set with the
  10015. * `.highcharts-background` class.
  10016. *
  10017. * @sample {highcharts} highcharts/chart/bordercolor/
  10018. * Brown border
  10019. * @sample {highstock} stock/chart/border/
  10020. * Brown border
  10021. * @sample {highmaps} maps/chart/border/
  10022. * Border options
  10023. *
  10024. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10025. */
  10026. borderColor: '#335cad',
  10027. /**
  10028. * The pixel width of the outer chart border.
  10029. *
  10030. * @see In styled mode, the stroke is set with the
  10031. * `.highcharts-background` class.
  10032. *
  10033. * @sample {highcharts} highcharts/chart/borderwidth/
  10034. * 5px border
  10035. * @sample {highstock} stock/chart/border/
  10036. * 2px border
  10037. * @sample {highmaps} maps/chart/border/
  10038. * Border options
  10039. *
  10040. * @type {number}
  10041. * @default 0
  10042. * @apioption chart.borderWidth
  10043. */
  10044. /**
  10045. * The background color or gradient for the outer chart area.
  10046. *
  10047. * @see In styled mode, the background is set with the
  10048. * `.highcharts-background` class.
  10049. *
  10050. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  10051. * Color
  10052. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  10053. * Gradient
  10054. * @sample {highstock} stock/chart/backgroundcolor-color/
  10055. * Color
  10056. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  10057. * Gradient
  10058. * @sample {highmaps} maps/chart/backgroundcolor-color/
  10059. * Color
  10060. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  10061. * Gradient
  10062. *
  10063. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10064. */
  10065. backgroundColor: '#ffffff',
  10066. /**
  10067. * The background color or gradient for the plot area.
  10068. *
  10069. * @see In styled mode, the plot background is set with the
  10070. * `.highcharts-plot-background` class.
  10071. *
  10072. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  10073. * Color
  10074. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  10075. * Gradient
  10076. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  10077. * Color
  10078. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  10079. * Gradient
  10080. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  10081. * Color
  10082. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  10083. * Gradient
  10084. *
  10085. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10086. * @apioption chart.plotBackgroundColor
  10087. */
  10088. /**
  10089. * The URL for an image to use as the plot background. To set an image
  10090. * as the background for the entire chart, set a CSS background image
  10091. * to the container element. Note that for the image to be applied to
  10092. * exported charts, its URL needs to be accessible by the export server.
  10093. *
  10094. * @see In styled mode, a plot background image can be set with the
  10095. * `.highcharts-plot-background` class and a [custom pattern](
  10096. * https://www.highcharts.com/docs/chart-design-and-style/
  10097. * gradients-shadows-and-patterns).
  10098. *
  10099. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  10100. * Skies
  10101. * @sample {highstock} stock/chart/plotbackgroundimage/
  10102. * Skies
  10103. *
  10104. * @type {string}
  10105. * @apioption chart.plotBackgroundImage
  10106. */
  10107. /**
  10108. * The color of the inner chart or plot area border.
  10109. *
  10110. * @see In styled mode, a plot border stroke can be set with the
  10111. * `.highcharts-plot-border` class.
  10112. *
  10113. * @sample {highcharts} highcharts/chart/plotbordercolor/
  10114. * Blue border
  10115. * @sample {highstock} stock/chart/plotborder/
  10116. * Blue border
  10117. * @sample {highmaps} maps/chart/plotborder/
  10118. * Plot border options
  10119. *
  10120. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10121. */
  10122. plotBorderColor: '#cccccc'
  10123. },
  10124. /**
  10125. * The chart's main title.
  10126. *
  10127. * @sample {highmaps} maps/title/title/
  10128. * Title options demonstrated
  10129. */
  10130. title: {
  10131. /**
  10132. * When the title is floating, the plot area will not move to make space
  10133. * for it.
  10134. *
  10135. * @sample {highcharts} highcharts/chart/zoomtype-none/
  10136. * False by default
  10137. * @sample {highcharts} highcharts/title/floating/
  10138. * True - title on top of the plot area
  10139. * @sample {highstock} stock/chart/title-floating/
  10140. * True - title on top of the plot area
  10141. *
  10142. * @type {boolean}
  10143. * @default false
  10144. * @since 2.1
  10145. * @apioption title.floating
  10146. */
  10147. /**
  10148. * CSS styles for the title. Use this for font styling, but use `align`,
  10149. * `x` and `y` for text alignment.
  10150. *
  10151. * In styled mode, the title style is given in the `.highcharts-title`
  10152. * class.
  10153. *
  10154. * @sample {highcharts} highcharts/title/style/
  10155. * Custom color and weight
  10156. * @sample {highstock} stock/chart/title-style/
  10157. * Custom color and weight
  10158. * @sample highcharts/css/titles/
  10159. * Styled mode
  10160. *
  10161. * @type {Highcharts.CSSObject}
  10162. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  10163. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  10164. * @apioption title.style
  10165. */
  10166. /**
  10167. * Whether to
  10168. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  10169. * to render the text.
  10170. *
  10171. * @type {boolean}
  10172. * @default false
  10173. * @apioption title.useHTML
  10174. */
  10175. /**
  10176. * The vertical alignment of the title. Can be one of `"top"`,
  10177. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  10178. * as if [floating](#title.floating) were `true`.
  10179. *
  10180. * @sample {highcharts} highcharts/title/verticalalign/
  10181. * Chart title in bottom right corner
  10182. * @sample {highstock} stock/chart/title-verticalalign/
  10183. * Chart title in bottom right corner
  10184. *
  10185. * @type {Highcharts.VerticalAlignValue}
  10186. * @since 2.1
  10187. * @apioption title.verticalAlign
  10188. */
  10189. /**
  10190. * The x position of the title relative to the alignment within
  10191. * `chart.spacingLeft` and `chart.spacingRight`.
  10192. *
  10193. * @sample {highcharts} highcharts/title/align/
  10194. * Aligned to the plot area (x = 70px = margin left - spacing
  10195. * left)
  10196. * @sample {highstock} stock/chart/title-align/
  10197. * Aligned to the plot area (x = 50px = margin left - spacing
  10198. * left)
  10199. *
  10200. * @type {number}
  10201. * @default 0
  10202. * @since 2.0
  10203. * @apioption title.x
  10204. */
  10205. /**
  10206. * The y position of the title relative to the alignment within
  10207. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  10208. * #chart.spacingBottom). By default it depends on the font size.
  10209. *
  10210. * @sample {highcharts} highcharts/title/y/
  10211. * Title inside the plot area
  10212. * @sample {highstock} stock/chart/title-verticalalign/
  10213. * Chart title in bottom right corner
  10214. *
  10215. * @type {number}
  10216. * @since 2.0
  10217. * @apioption title.y
  10218. */
  10219. /**
  10220. * The title of the chart. To disable the title, set the `text` to
  10221. * `undefined`.
  10222. *
  10223. * @sample {highcharts} highcharts/title/text/
  10224. * Custom title
  10225. * @sample {highstock} stock/chart/title-text/
  10226. * Custom title
  10227. *
  10228. * @default {highcharts|highmaps} Chart title
  10229. * @default {highstock} undefined
  10230. */
  10231. text: 'Chart title',
  10232. /**
  10233. * The horizontal alignment of the title. Can be one of "left", "center"
  10234. * and "right".
  10235. *
  10236. * @sample {highcharts} highcharts/title/align/
  10237. * Aligned to the plot area (x = 70px = margin left - spacing
  10238. * left)
  10239. * @sample {highstock} stock/chart/title-align/
  10240. * Aligned to the plot area (x = 50px = margin left - spacing
  10241. * left)
  10242. *
  10243. * @type {Highcharts.AlignValue}
  10244. * @since 2.0
  10245. */
  10246. align: 'center',
  10247. /**
  10248. * The margin between the title and the plot area, or if a subtitle
  10249. * is present, the margin between the subtitle and the plot area.
  10250. *
  10251. * @sample {highcharts} highcharts/title/margin-50/
  10252. * A chart title margin of 50
  10253. * @sample {highcharts} highcharts/title/margin-subtitle/
  10254. * The same margin applied with a subtitle
  10255. * @sample {highstock} stock/chart/title-margin/
  10256. * A chart title margin of 50
  10257. *
  10258. * @since 2.1
  10259. */
  10260. margin: 15,
  10261. /**
  10262. * Adjustment made to the title width, normally to reserve space for
  10263. * the exporting burger menu.
  10264. *
  10265. * @sample highcharts/title/widthadjust/
  10266. * Wider menu, greater padding
  10267. *
  10268. * @since 4.2.5
  10269. */
  10270. widthAdjust: -44
  10271. },
  10272. /**
  10273. * The chart's subtitle. This can be used both to display a subtitle below
  10274. * the main title, and to display random text anywhere in the chart. The
  10275. * subtitle can be updated after chart initialization through the
  10276. * `Chart.setTitle` method.
  10277. *
  10278. * @sample {highmaps} maps/title/subtitle/
  10279. * Subtitle options demonstrated
  10280. */
  10281. subtitle: {
  10282. /**
  10283. * When the subtitle is floating, the plot area will not move to make
  10284. * space for it.
  10285. *
  10286. * @sample {highcharts} highcharts/subtitle/floating/
  10287. * Floating title and subtitle
  10288. * @sample {highstock} stock/chart/subtitle-footnote
  10289. * Footnote floating at bottom right of plot area
  10290. *
  10291. * @type {boolean}
  10292. * @default false
  10293. * @since 2.1
  10294. * @apioption subtitle.floating
  10295. */
  10296. /**
  10297. * CSS styles for the title.
  10298. *
  10299. * In styled mode, the subtitle style is given in the
  10300. * `.highcharts-subtitle` class.
  10301. *
  10302. * @sample {highcharts} highcharts/subtitle/style/
  10303. * Custom color and weight
  10304. * @sample {highcharts} highcharts/css/titles/
  10305. * Styled mode
  10306. * @sample {highstock} stock/chart/subtitle-style
  10307. * Custom color and weight
  10308. * @sample {highstock} highcharts/css/titles/
  10309. * Styled mode
  10310. * @sample {highmaps} highcharts/css/titles/
  10311. * Styled mode
  10312. *
  10313. * @type {Highcharts.CSSObject}
  10314. * @default {"color": "#666666"}
  10315. * @apioption subtitle.style
  10316. */
  10317. /**
  10318. * Whether to
  10319. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  10320. * to render the text.
  10321. *
  10322. * @type {boolean}
  10323. * @default false
  10324. * @apioption subtitle.useHTML
  10325. */
  10326. /**
  10327. * The vertical alignment of the title. Can be one of `"top"`,
  10328. * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
  10329. * floating.
  10330. *
  10331. * @sample {highcharts} highcharts/subtitle/verticalalign/
  10332. * Footnote at the bottom right of plot area
  10333. * @sample {highstock} stock/chart/subtitle-footnote
  10334. * Footnote at the bottom right of plot area
  10335. *
  10336. * @type {Highcharts.VerticalAlignValue}
  10337. * @since 2.1
  10338. * @apioption subtitle.verticalAlign
  10339. */
  10340. /**
  10341. * The x position of the subtitle relative to the alignment within
  10342. * `chart.spacingLeft` and `chart.spacingRight`.
  10343. *
  10344. * @sample {highcharts} highcharts/subtitle/align/
  10345. * Footnote at right of plot area
  10346. * @sample {highstock} stock/chart/subtitle-footnote
  10347. * Footnote at the bottom right of plot area
  10348. *
  10349. * @type {number}
  10350. * @default 0
  10351. * @since 2.0
  10352. * @apioption subtitle.x
  10353. */
  10354. /**
  10355. * The y position of the subtitle relative to the alignment within
  10356. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  10357. * is laid out below the title unless the title is floating.
  10358. *
  10359. * @sample {highcharts} highcharts/subtitle/verticalalign/
  10360. * Footnote at the bottom right of plot area
  10361. * @sample {highstock} stock/chart/subtitle-footnote
  10362. * Footnote at the bottom right of plot area
  10363. *
  10364. * @type {number}
  10365. * @since 2.0
  10366. * @apioption subtitle.y
  10367. */
  10368. /**
  10369. * The subtitle of the chart.
  10370. *
  10371. * @sample {highcharts|highstock} highcharts/subtitle/text/
  10372. * Custom subtitle
  10373. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  10374. * Formatted and linked text.
  10375. */
  10376. text: '',
  10377. /**
  10378. * The horizontal alignment of the subtitle. Can be one of "left",
  10379. * "center" and "right".
  10380. *
  10381. * @sample {highcharts} highcharts/subtitle/align/
  10382. * Footnote at right of plot area
  10383. * @sample {highstock} stock/chart/subtitle-footnote
  10384. * Footnote at bottom right of plot area
  10385. *
  10386. * @type {Highcharts.AlignValue}
  10387. * @since 2.0
  10388. */
  10389. align: 'center',
  10390. /**
  10391. * Adjustment made to the subtitle width, normally to reserve space
  10392. * for the exporting burger menu.
  10393. *
  10394. * @see [title.widthAdjust](#title.widthAdjust)
  10395. *
  10396. * @sample highcharts/title/widthadjust/
  10397. * Wider menu, greater padding
  10398. *
  10399. * @since 4.2.5
  10400. */
  10401. widthAdjust: -44
  10402. },
  10403. /**
  10404. * The chart's caption, which will render below the chart and will be part
  10405. * of exported charts. The caption can be updated after chart initialization
  10406. * through the `Chart.update` or `Chart.caption.update` methods.
  10407. *
  10408. * @sample highcharts/caption/text/
  10409. * A chart with a caption
  10410. * @since 7.2.0
  10411. */
  10412. caption: {
  10413. /**
  10414. * When the caption is floating, the plot area will not move to make
  10415. * space for it.
  10416. *
  10417. * @type {boolean}
  10418. * @default false
  10419. * @apioption caption.floating
  10420. */
  10421. /**
  10422. * The margin between the caption and the plot area.
  10423. */
  10424. margin: 15,
  10425. /**
  10426. * CSS styles for the caption.
  10427. *
  10428. * In styled mode, the caption style is given in the
  10429. * `.highcharts-caption` class.
  10430. *
  10431. * @sample {highcharts} highcharts/css/titles/
  10432. * Styled mode
  10433. *
  10434. * @type {Highcharts.CSSObject}
  10435. * @default {"color": "#666666"}
  10436. * @apioption caption.style
  10437. */
  10438. /**
  10439. * Whether to
  10440. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  10441. * to render the text.
  10442. *
  10443. * @type {boolean}
  10444. * @default false
  10445. * @apioption caption.useHTML
  10446. */
  10447. /**
  10448. * The x position of the caption relative to the alignment within
  10449. * `chart.spacingLeft` and `chart.spacingRight`.
  10450. *
  10451. * @type {number}
  10452. * @default 0
  10453. * @apioption caption.x
  10454. */
  10455. /**
  10456. * The y position of the caption relative to the alignment within
  10457. * `chart.spacingTop` and `chart.spacingBottom`.
  10458. *
  10459. * @type {number}
  10460. * @apioption caption.y
  10461. */
  10462. /**
  10463. * The caption text of the chart.
  10464. *
  10465. * @sample {highcharts} highcharts/caption/text/
  10466. * Custom caption
  10467. */
  10468. text: '',
  10469. /**
  10470. * The horizontal alignment of the caption. Can be one of "left",
  10471. * "center" and "right".
  10472. *
  10473. * @type {Highcharts.AlignValue}
  10474. */
  10475. align: 'left',
  10476. /**
  10477. * The vertical alignment of the caption. Can be one of `"top"`,
  10478. * `"middle"` and `"bottom"`. When middle, the caption behaves as
  10479. * floating.
  10480. *
  10481. * @type {Highcharts.VerticalAlignValue}
  10482. */
  10483. verticalAlign: 'bottom'
  10484. },
  10485. /**
  10486. * The plotOptions is a wrapper object for config objects for each series
  10487. * type. The config objects for each series can also be overridden for
  10488. * each series item as given in the series array.
  10489. *
  10490. * Configuration options for the series are given in three levels. Options
  10491. * for all series in a chart are given in the [plotOptions.series](
  10492. * #plotOptions.series) object. Then options for all series of a specific
  10493. * type are given in the plotOptions of that type, for example
  10494. * `plotOptions.line`. Next, options for one single series are given in
  10495. * [the series array](#series).
  10496. */
  10497. plotOptions: {},
  10498. /**
  10499. * HTML labels that can be positioned anywhere in the chart area.
  10500. *
  10501. * This option is deprecated since v7.1.2. Instead, use
  10502. * [annotations](#annotations) that support labels.
  10503. *
  10504. * @deprecated
  10505. * @product highcharts highstock
  10506. */
  10507. labels: {
  10508. /**
  10509. * An HTML label that can be positioned anywhere in the chart area.
  10510. *
  10511. * @deprecated
  10512. * @type {Array<*>}
  10513. * @apioption labels.items
  10514. */
  10515. /**
  10516. * Inner HTML or text for the label.
  10517. *
  10518. * @deprecated
  10519. * @type {string}
  10520. * @apioption labels.items.html
  10521. */
  10522. /**
  10523. * CSS styles for each label. To position the label, use left and top
  10524. * like this:
  10525. * ```js
  10526. * style: {
  10527. * left: '100px',
  10528. * top: '100px'
  10529. * }
  10530. * ```
  10531. *
  10532. * @deprecated
  10533. * @type {Highcharts.CSSObject}
  10534. * @apioption labels.items.style
  10535. */
  10536. /**
  10537. * Shared CSS styles for all labels.
  10538. *
  10539. * @deprecated
  10540. * @type {Highcharts.CSSObject}
  10541. * @default {"color": "#333333", "position": "absolute"}
  10542. */
  10543. style: {
  10544. /**
  10545. * @ignore-option
  10546. */
  10547. position: 'absolute',
  10548. /**
  10549. * @ignore-option
  10550. */
  10551. color: '#333333'
  10552. }
  10553. },
  10554. /**
  10555. * The legend is a box containing a symbol and name for each series
  10556. * item or point item in the chart. Each series (or points in case
  10557. * of pie charts) is represented by a symbol and its name in the legend.
  10558. *
  10559. * It is possible to override the symbol creator function and create
  10560. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  10561. *
  10562. * @productdesc {highmaps}
  10563. * A Highmaps legend by default contains one legend item per series, but if
  10564. * a `colorAxis` is defined, the axis will be displayed in the legend.
  10565. * Either as a gradient, or as multiple legend items for `dataClasses`.
  10566. */
  10567. legend: {
  10568. /**
  10569. * The background color of the legend.
  10570. *
  10571. * @see In styled mode, the legend background fill can be applied with
  10572. * the `.highcharts-legend-box` class.
  10573. *
  10574. * @sample {highcharts} highcharts/legend/backgroundcolor/
  10575. * Yellowish background
  10576. * @sample {highstock} stock/legend/align/
  10577. * Various legend options
  10578. * @sample {highmaps} maps/legend/border-background/
  10579. * Border and background options
  10580. *
  10581. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10582. * @apioption legend.backgroundColor
  10583. */
  10584. /**
  10585. * The width of the drawn border around the legend.
  10586. *
  10587. * @see In styled mode, the legend border stroke width can be applied
  10588. * with the `.highcharts-legend-box` class.
  10589. *
  10590. * @sample {highcharts} highcharts/legend/borderwidth/
  10591. * 2px border width
  10592. * @sample {highstock} stock/legend/align/
  10593. * Various legend options
  10594. * @sample {highmaps} maps/legend/border-background/
  10595. * Border and background options
  10596. *
  10597. * @type {number}
  10598. * @default 0
  10599. * @apioption legend.borderWidth
  10600. */
  10601. /**
  10602. * Enable or disable the legend. There is also a series-specific option,
  10603. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  10604. * series from the legend. In some series types this is `false` by
  10605. * default, so it must set to `true` in order to show the legend for the
  10606. * series.
  10607. *
  10608. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  10609. * @sample {highstock} stock/legend/align/ Various legend options
  10610. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  10611. *
  10612. * @default {highstock} false
  10613. * @default {highmaps} true
  10614. * @default {gantt} false
  10615. */
  10616. enabled: true,
  10617. /**
  10618. * The horizontal alignment of the legend box within the chart area.
  10619. * Valid values are `left`, `center` and `right`.
  10620. *
  10621. * In the case that the legend is aligned in a corner position, the
  10622. * `layout` option will determine whether to place it above/below
  10623. * or on the side of the plot area.
  10624. *
  10625. * @sample {highcharts} highcharts/legend/align/
  10626. * Legend at the right of the chart
  10627. * @sample {highstock} stock/legend/align/
  10628. * Various legend options
  10629. * @sample {highmaps} maps/legend/alignment/
  10630. * Legend alignment
  10631. *
  10632. * @type {Highcharts.AlignValue}
  10633. * @since 2.0
  10634. */
  10635. align: 'center',
  10636. /**
  10637. * If the [layout](legend.layout) is `horizontal` and the legend items
  10638. * span over two lines or more, whether to align the items into vertical
  10639. * columns. Setting this to `false` makes room for more items, but will
  10640. * look more messy.
  10641. *
  10642. * @since 6.1.0
  10643. */
  10644. alignColumns: true,
  10645. /**
  10646. * When the legend is floating, the plot area ignores it and is allowed
  10647. * to be placed below it.
  10648. *
  10649. * @sample {highcharts} highcharts/legend/floating-false/
  10650. * False by default
  10651. * @sample {highcharts} highcharts/legend/floating-true/
  10652. * True
  10653. * @sample {highmaps} maps/legend/alignment/
  10654. * Floating legend
  10655. *
  10656. * @type {boolean}
  10657. * @default false
  10658. * @since 2.1
  10659. * @apioption legend.floating
  10660. */
  10661. /**
  10662. * The layout of the legend items. Can be one of `horizontal` or
  10663. * `vertical` or `proximate`. When `proximate`, the legend items will be
  10664. * placed as close as possible to the graphs they're representing,
  10665. * except in inverted charts or when the legend position doesn't allow
  10666. * it.
  10667. *
  10668. * @sample {highcharts} highcharts/legend/layout-horizontal/
  10669. * Horizontal by default
  10670. * @sample {highcharts} highcharts/legend/layout-vertical/
  10671. * Vertical
  10672. * @sample highcharts/legend/layout-proximate
  10673. * Labels proximate to the data
  10674. * @sample {highstock} stock/legend/layout-horizontal/
  10675. * Horizontal by default
  10676. * @sample {highmaps} maps/legend/padding-itemmargin/
  10677. * Vertical with data classes
  10678. * @sample {highmaps} maps/legend/layout-vertical/
  10679. * Vertical with color axis gradient
  10680. *
  10681. * @validvalue ["horizontal", "vertical", "proximate"]
  10682. */
  10683. layout: 'horizontal',
  10684. /**
  10685. * In a legend with horizontal layout, the itemDistance defines the
  10686. * pixel distance between each item.
  10687. *
  10688. * @sample {highcharts} highcharts/legend/layout-horizontal/
  10689. * 50px item distance
  10690. * @sample {highstock} highcharts/legend/layout-horizontal/
  10691. * 50px item distance
  10692. *
  10693. * @type {number}
  10694. * @default {highcharts} 20
  10695. * @default {highstock} 20
  10696. * @default {highmaps} 8
  10697. * @since 3.0.3
  10698. * @apioption legend.itemDistance
  10699. */
  10700. /**
  10701. * The pixel bottom margin for each legend item.
  10702. *
  10703. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10704. * Padding and item margins demonstrated
  10705. * @sample {highmaps} maps/legend/padding-itemmargin/
  10706. * Padding and item margins demonstrated
  10707. *
  10708. * @type {number}
  10709. * @default 0
  10710. * @since 2.2.0
  10711. * @apioption legend.itemMarginBottom
  10712. */
  10713. /**
  10714. * The pixel top margin for each legend item.
  10715. *
  10716. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10717. * Padding and item margins demonstrated
  10718. * @sample {highmaps} maps/legend/padding-itemmargin/
  10719. * Padding and item margins demonstrated
  10720. *
  10721. * @type {number}
  10722. * @default 0
  10723. * @since 2.2.0
  10724. * @apioption legend.itemMarginTop
  10725. */
  10726. /**
  10727. * The width for each legend item. By default the items are laid out
  10728. * successively. In a [horizontal layout](legend.layout), if the items
  10729. * are laid out across two rows or more, they will be vertically aligned
  10730. * depending on the [legend.alignColumns](legend.alignColumns) option.
  10731. *
  10732. * @sample {highcharts} highcharts/legend/itemwidth-default/
  10733. * Undefined by default
  10734. * @sample {highcharts} highcharts/legend/itemwidth-80/
  10735. * 80 for aligned legend items
  10736. *
  10737. * @type {number}
  10738. * @since 2.0
  10739. * @apioption legend.itemWidth
  10740. */
  10741. /**
  10742. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  10743. * for each legend label. Available variables relates to properties on
  10744. * the series, or the point in case of pies.
  10745. *
  10746. * @type {string}
  10747. * @default {name}
  10748. * @since 1.3
  10749. * @apioption legend.labelFormat
  10750. */
  10751. /* eslint-disable valid-jsdoc */
  10752. /**
  10753. * Callback function to format each of the series' labels. The `this`
  10754. * keyword refers to the series object, or the point object in case of
  10755. * pie charts. By default the series or point name is printed.
  10756. *
  10757. * @productdesc {highmaps}
  10758. * In Highmaps the context can also be a data class in case of a
  10759. * `colorAxis`.
  10760. *
  10761. * @sample {highcharts} highcharts/legend/labelformatter/
  10762. * Add text
  10763. * @sample {highmaps} maps/legend/labelformatter/
  10764. * Data classes with label formatter
  10765. *
  10766. * @type {Highcharts.FormatterCallbackFunction<Point|Series>}
  10767. */
  10768. labelFormatter: function () {
  10769. /** eslint-enable valid-jsdoc */
  10770. return this.name;
  10771. },
  10772. /**
  10773. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  10774. * the line height for each item can be set using
  10775. * `itemStyle.lineHeight`, and the padding between items using
  10776. * `itemMarginTop` and `itemMarginBottom`.
  10777. *
  10778. * @sample {highcharts} highcharts/legend/lineheight/
  10779. * Setting padding
  10780. *
  10781. * @deprecated
  10782. *
  10783. * @type {number}
  10784. * @default 16
  10785. * @since 2.0
  10786. * @product highcharts gantt
  10787. * @apioption legend.lineHeight
  10788. */
  10789. /**
  10790. * If the plot area sized is calculated automatically and the legend is
  10791. * not floating, the legend margin is the space between the legend and
  10792. * the axis labels or plot area.
  10793. *
  10794. * @sample {highcharts} highcharts/legend/margin-default/
  10795. * 12 pixels by default
  10796. * @sample {highcharts} highcharts/legend/margin-30/
  10797. * 30 pixels
  10798. *
  10799. * @type {number}
  10800. * @default 12
  10801. * @since 2.1
  10802. * @apioption legend.margin
  10803. */
  10804. /**
  10805. * Maximum pixel height for the legend. When the maximum height is
  10806. * extended, navigation will show.
  10807. *
  10808. * @type {number}
  10809. * @since 2.3.0
  10810. * @apioption legend.maxHeight
  10811. */
  10812. /**
  10813. * The color of the drawn border around the legend.
  10814. *
  10815. * @see In styled mode, the legend border stroke can be applied with the
  10816. * `.highcharts-legend-box` class.
  10817. *
  10818. * @sample {highcharts} highcharts/legend/bordercolor/
  10819. * Brown border
  10820. * @sample {highstock} stock/legend/align/
  10821. * Various legend options
  10822. * @sample {highmaps} maps/legend/border-background/
  10823. * Border and background options
  10824. *
  10825. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10826. */
  10827. borderColor: '#999999',
  10828. /**
  10829. * The border corner radius of the legend.
  10830. *
  10831. * @sample {highcharts} highcharts/legend/borderradius-default/
  10832. * Square by default
  10833. * @sample {highcharts} highcharts/legend/borderradius-round/
  10834. * 5px rounded
  10835. * @sample {highmaps} maps/legend/border-background/
  10836. * Border and background options
  10837. */
  10838. borderRadius: 0,
  10839. /**
  10840. * Options for the paging or navigation appearing when the legend is
  10841. * overflown. Navigation works well on screen, but not in static
  10842. * exported images. One way of working around that is to
  10843. * [increase the chart height in
  10844. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  10845. */
  10846. navigation: {
  10847. /**
  10848. * How to animate the pages when navigating up or down. A value of
  10849. * `true` applies the default navigation given in the
  10850. * `chart.animation` option. Additional options can be given as an
  10851. * object containing values for easing and duration.
  10852. *
  10853. * @sample {highcharts} highcharts/legend/navigation/
  10854. * Legend page navigation demonstrated
  10855. * @sample {highstock} highcharts/legend/navigation/
  10856. * Legend page navigation demonstrated
  10857. *
  10858. * @type {boolean|Highcharts.AnimationOptionsObject}
  10859. * @default true
  10860. * @since 2.2.4
  10861. * @apioption legend.navigation.animation
  10862. */
  10863. /**
  10864. * The pixel size of the up and down arrows in the legend paging
  10865. * navigation.
  10866. *
  10867. * @sample {highcharts} highcharts/legend/navigation/
  10868. * Legend page navigation demonstrated
  10869. * @sample {highstock} highcharts/legend/navigation/
  10870. * Legend page navigation demonstrated
  10871. *
  10872. * @type {number}
  10873. * @default 12
  10874. * @since 2.2.4
  10875. * @apioption legend.navigation.arrowSize
  10876. */
  10877. /**
  10878. * Whether to enable the legend navigation. In most cases, disabling
  10879. * the navigation results in an unwanted overflow.
  10880. *
  10881. * See also the [adapt chart to legend](
  10882. * https://www.highcharts.com/products/plugin-registry/single/8/Adapt-Chart-To-Legend)
  10883. * plugin for a solution to extend the chart height to make room for
  10884. * the legend, optionally in exported charts only.
  10885. *
  10886. * @type {boolean}
  10887. * @default true
  10888. * @since 4.2.4
  10889. * @apioption legend.navigation.enabled
  10890. */
  10891. /**
  10892. * Text styles for the legend page navigation.
  10893. *
  10894. * @see In styled mode, the navigation items are styled with the
  10895. * `.highcharts-legend-navigation` class.
  10896. *
  10897. * @sample {highcharts} highcharts/legend/navigation/
  10898. * Legend page navigation demonstrated
  10899. * @sample {highstock} highcharts/legend/navigation/
  10900. * Legend page navigation demonstrated
  10901. *
  10902. * @type {Highcharts.CSSObject}
  10903. * @since 2.2.4
  10904. * @apioption legend.navigation.style
  10905. */
  10906. /**
  10907. * The color for the active up or down arrow in the legend page
  10908. * navigation.
  10909. *
  10910. * @see In styled mode, the active arrow be styled with the
  10911. * `.highcharts-legend-nav-active` class.
  10912. *
  10913. * @sample {highcharts} highcharts/legend/navigation/
  10914. * Legend page navigation demonstrated
  10915. * @sample {highstock} highcharts/legend/navigation/
  10916. * Legend page navigation demonstrated
  10917. *
  10918. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10919. * @since 2.2.4
  10920. */
  10921. activeColor: '#003399',
  10922. /**
  10923. * The color of the inactive up or down arrow in the legend page
  10924. * navigation. .
  10925. *
  10926. * @see In styled mode, the inactive arrow be styled with the
  10927. * `.highcharts-legend-nav-inactive` class.
  10928. *
  10929. * @sample {highcharts} highcharts/legend/navigation/
  10930. * Legend page navigation demonstrated
  10931. * @sample {highstock} highcharts/legend/navigation/
  10932. * Legend page navigation demonstrated
  10933. *
  10934. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10935. * @since 2.2.4
  10936. */
  10937. inactiveColor: '#cccccc'
  10938. },
  10939. /**
  10940. * The inner padding of the legend box.
  10941. *
  10942. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10943. * Padding and item margins demonstrated
  10944. * @sample {highmaps} maps/legend/padding-itemmargin/
  10945. * Padding and item margins demonstrated
  10946. *
  10947. * @type {number}
  10948. * @default 8
  10949. * @since 2.2.0
  10950. * @apioption legend.padding
  10951. */
  10952. /**
  10953. * Whether to reverse the order of the legend items compared to the
  10954. * order of the series or points as defined in the configuration object.
  10955. *
  10956. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  10957. * [series.legendIndex](#series.legendIndex)
  10958. *
  10959. * @sample {highcharts} highcharts/legend/reversed/
  10960. * Stacked bar with reversed legend
  10961. *
  10962. * @type {boolean}
  10963. * @default false
  10964. * @since 1.2.5
  10965. * @apioption legend.reversed
  10966. */
  10967. /**
  10968. * Whether to show the symbol on the right side of the text rather than
  10969. * the left side. This is common in Arabic and Hebraic.
  10970. *
  10971. * @sample {highcharts} highcharts/legend/rtl/
  10972. * Symbol to the right
  10973. *
  10974. * @type {boolean}
  10975. * @default false
  10976. * @since 2.2
  10977. * @apioption legend.rtl
  10978. */
  10979. /**
  10980. * CSS styles for the legend area. In the 1.x versions the position
  10981. * of the legend area was determined by CSS. In 2.x, the position is
  10982. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  10983. * but the styles are still parsed for backwards compatibility.
  10984. *
  10985. * @deprecated
  10986. *
  10987. * @type {Highcharts.CSSObject}
  10988. * @product highcharts highstock
  10989. * @apioption legend.style
  10990. */
  10991. /**
  10992. * CSS styles for each legend item. Only a subset of CSS is supported,
  10993. * notably those options related to text. The default `textOverflow`
  10994. * property makes long texts truncate. Set it to `undefined` to wrap
  10995. * text instead. A `width` property can be added to control the text
  10996. * width.
  10997. *
  10998. * @see In styled mode, the legend items can be styled with the
  10999. * `.highcharts-legend-item` class.
  11000. *
  11001. * @sample {highcharts} highcharts/legend/itemstyle/
  11002. * Bold black text
  11003. * @sample {highmaps} maps/legend/itemstyle/
  11004. * Item text styles
  11005. *
  11006. * @type {Highcharts.CSSObject}
  11007. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "bold", "textOverflow": "ellipsis"}
  11008. */
  11009. itemStyle: {
  11010. /**
  11011. * @ignore
  11012. */
  11013. color: '#333333',
  11014. /**
  11015. * @ignore
  11016. */
  11017. cursor: 'pointer',
  11018. /**
  11019. * @ignore
  11020. */
  11021. fontSize: '12px',
  11022. /**
  11023. * @ignore
  11024. */
  11025. fontWeight: 'bold',
  11026. /**
  11027. * @ignore
  11028. */
  11029. textOverflow: 'ellipsis'
  11030. },
  11031. /**
  11032. * CSS styles for each legend item in hover mode. Only a subset of
  11033. * CSS is supported, notably those options related to text. Properties
  11034. * are inherited from `style` unless overridden here.
  11035. *
  11036. * @see In styled mode, the hovered legend items can be styled with
  11037. * the `.highcharts-legend-item:hover` pesudo-class.
  11038. *
  11039. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  11040. * Red on hover
  11041. * @sample {highmaps} maps/legend/itemstyle/
  11042. * Item text styles
  11043. *
  11044. * @type {Highcharts.CSSObject}
  11045. * @default {"color": "#000000"}
  11046. */
  11047. itemHoverStyle: {
  11048. /**
  11049. * @ignore
  11050. */
  11051. color: '#000000'
  11052. },
  11053. /**
  11054. * CSS styles for each legend item when the corresponding series or
  11055. * point is hidden. Only a subset of CSS is supported, notably those
  11056. * options related to text. Properties are inherited from `style`
  11057. * unless overridden here.
  11058. *
  11059. * @see In styled mode, the hidden legend items can be styled with
  11060. * the `.highcharts-legend-item-hidden` class.
  11061. *
  11062. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  11063. * Darker gray color
  11064. *
  11065. * @type {Highcharts.CSSObject}
  11066. * @default {"color": "#cccccc"}
  11067. */
  11068. itemHiddenStyle: {
  11069. /**
  11070. * @ignore
  11071. */
  11072. color: '#cccccc'
  11073. },
  11074. /**
  11075. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  11076. * also needs to be applied for this to take effect. The shadow can be
  11077. * an object configuration containing `color`, `offsetX`, `offsetY`,
  11078. * `opacity` and `width`.
  11079. *
  11080. * @sample {highcharts} highcharts/legend/shadow/
  11081. * White background and drop shadow
  11082. * @sample {highstock} stock/legend/align/
  11083. * Various legend options
  11084. * @sample {highmaps} maps/legend/border-background/
  11085. * Border and background options
  11086. *
  11087. * @type {boolean|Highcharts.CSSObject}
  11088. */
  11089. shadow: false,
  11090. /**
  11091. * Default styling for the checkbox next to a legend item when
  11092. * `showCheckbox` is true.
  11093. *
  11094. * @type {Highcharts.CSSObject}
  11095. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  11096. */
  11097. itemCheckboxStyle: {
  11098. /**
  11099. * @ignore
  11100. */
  11101. position: 'absolute',
  11102. /**
  11103. * @ignore
  11104. */
  11105. width: '13px',
  11106. /**
  11107. * @ignore
  11108. */
  11109. height: '13px'
  11110. },
  11111. // itemWidth: undefined,
  11112. /**
  11113. * When this is true, the legend symbol width will be the same as
  11114. * the symbol height, which in turn defaults to the font size of the
  11115. * legend items.
  11116. *
  11117. * @since 5.0.0
  11118. */
  11119. squareSymbol: true,
  11120. /**
  11121. * The pixel height of the symbol for series types that use a rectangle
  11122. * in the legend. Defaults to the font size of legend items.
  11123. *
  11124. * @productdesc {highmaps}
  11125. * In Highmaps, when the symbol is the gradient of a vertical color
  11126. * axis, the height defaults to 200.
  11127. *
  11128. * @sample {highmaps} maps/legend/layout-vertical-sized/
  11129. * Sized vertical gradient
  11130. * @sample {highmaps} maps/legend/padding-itemmargin/
  11131. * No distance between data classes
  11132. *
  11133. * @type {number}
  11134. * @since 3.0.8
  11135. * @apioption legend.symbolHeight
  11136. */
  11137. /**
  11138. * The border radius of the symbol for series types that use a rectangle
  11139. * in the legend. Defaults to half the `symbolHeight`.
  11140. *
  11141. * @sample {highcharts} highcharts/legend/symbolradius/
  11142. * Round symbols
  11143. * @sample {highstock} highcharts/legend/symbolradius/
  11144. * Round symbols
  11145. * @sample {highmaps} highcharts/legend/symbolradius/
  11146. * Round symbols
  11147. *
  11148. * @type {number}
  11149. * @since 3.0.8
  11150. * @apioption legend.symbolRadius
  11151. */
  11152. /**
  11153. * The pixel width of the legend item symbol. When the `squareSymbol`
  11154. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  11155. *
  11156. * @productdesc {highmaps}
  11157. * In Highmaps, when the symbol is the gradient of a horizontal color
  11158. * axis, the width defaults to 200.
  11159. *
  11160. * @sample {highcharts} highcharts/legend/symbolwidth/
  11161. * Greater symbol width and padding
  11162. * @sample {highmaps} maps/legend/padding-itemmargin/
  11163. * Padding and item margins demonstrated
  11164. * @sample {highmaps} maps/legend/layout-vertical-sized/
  11165. * Sized vertical gradient
  11166. *
  11167. * @type {number}
  11168. * @apioption legend.symbolWidth
  11169. */
  11170. /**
  11171. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  11172. * to render the legend item texts.
  11173. *
  11174. * Prior to 4.1.7, when using HTML, [legend.navigation](
  11175. * #legend.navigation) was disabled.
  11176. *
  11177. * @type {boolean}
  11178. * @default false
  11179. * @apioption legend.useHTML
  11180. */
  11181. /**
  11182. * The width of the legend box. If a number is set, it translates to
  11183. * pixels. Since v7.0.2 it allows setting a percent string of the full
  11184. * chart width, for example `40%`.
  11185. *
  11186. * Defaults to the full chart width from legends below or above the
  11187. * chart, half the chart width for legends to the left and right.
  11188. *
  11189. * @sample {highcharts} highcharts/legend/width/
  11190. * Aligned to the plot area
  11191. * @sample {highcharts} highcharts/legend/width-percent/
  11192. * A percent of the chart width
  11193. *
  11194. * @type {number|string}
  11195. * @since 2.0
  11196. * @apioption legend.width
  11197. */
  11198. /**
  11199. * The pixel padding between the legend item symbol and the legend
  11200. * item text.
  11201. *
  11202. * @sample {highcharts} highcharts/legend/symbolpadding/
  11203. * Greater symbol width and padding
  11204. */
  11205. symbolPadding: 5,
  11206. /**
  11207. * The vertical alignment of the legend box. Can be one of `top`,
  11208. * `middle` or `bottom`. Vertical position can be further determined
  11209. * by the `y` option.
  11210. *
  11211. * In the case that the legend is aligned in a corner position, the
  11212. * `layout` option will determine whether to place it above/below
  11213. * or on the side of the plot area.
  11214. *
  11215. * When the [layout](#legend.layout) option is `proximate`, the
  11216. * `verticalAlign` option doesn't apply.
  11217. *
  11218. * @sample {highcharts} highcharts/legend/verticalalign/
  11219. * Legend 100px from the top of the chart
  11220. * @sample {highstock} stock/legend/align/
  11221. * Various legend options
  11222. * @sample {highmaps} maps/legend/alignment/
  11223. * Legend alignment
  11224. *
  11225. * @type {Highcharts.VerticalAlignValue}
  11226. * @since 2.0
  11227. */
  11228. verticalAlign: 'bottom',
  11229. // width: undefined,
  11230. /**
  11231. * The x offset of the legend relative to its horizontal alignment
  11232. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  11233. * x moves it to the left, positive x moves it to the right.
  11234. *
  11235. * @sample {highcharts} highcharts/legend/width/
  11236. * Aligned to the plot area
  11237. *
  11238. * @since 2.0
  11239. */
  11240. x: 0,
  11241. /**
  11242. * The vertical offset of the legend relative to it's vertical alignment
  11243. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  11244. * Negative y moves it up, positive y moves it down.
  11245. *
  11246. * @sample {highcharts} highcharts/legend/verticalalign/
  11247. * Legend 100px from the top of the chart
  11248. * @sample {highstock} stock/legend/align/
  11249. * Various legend options
  11250. * @sample {highmaps} maps/legend/alignment/
  11251. * Legend alignment
  11252. *
  11253. * @since 2.0
  11254. */
  11255. y: 0,
  11256. /**
  11257. * A title to be added on top of the legend.
  11258. *
  11259. * @sample {highcharts} highcharts/legend/title/
  11260. * Legend title
  11261. * @sample {highmaps} maps/legend/alignment/
  11262. * Legend with title
  11263. *
  11264. * @since 3.0
  11265. */
  11266. title: {
  11267. /**
  11268. * A text or HTML string for the title.
  11269. *
  11270. * @type {string}
  11271. * @since 3.0
  11272. * @apioption legend.title.text
  11273. */
  11274. /**
  11275. * Generic CSS styles for the legend title.
  11276. *
  11277. * @see In styled mode, the legend title is styled with the
  11278. * `.highcharts-legend-title` class.
  11279. *
  11280. * @type {Highcharts.CSSObject}
  11281. * @default {"fontWeight": "bold"}
  11282. * @since 3.0
  11283. */
  11284. style: {
  11285. /**
  11286. * @ignore
  11287. */
  11288. fontWeight: 'bold'
  11289. }
  11290. }
  11291. },
  11292. /**
  11293. * The loading options control the appearance of the loading screen
  11294. * that covers the plot area on chart operations. This screen only
  11295. * appears after an explicit call to `chart.showLoading()`. It is a
  11296. * utility for developers to communicate to the end user that something
  11297. * is going on, for example while retrieving new data via an XHR connection.
  11298. * The "Loading..." text itself is not part of this configuration
  11299. * object, but part of the `lang` object.
  11300. */
  11301. loading: {
  11302. /**
  11303. * The duration in milliseconds of the fade out effect.
  11304. *
  11305. * @sample highcharts/loading/hideduration/
  11306. * Fade in and out over a second
  11307. *
  11308. * @type {number}
  11309. * @default 100
  11310. * @since 1.2.0
  11311. * @apioption loading.hideDuration
  11312. */
  11313. /**
  11314. * The duration in milliseconds of the fade in effect.
  11315. *
  11316. * @sample highcharts/loading/hideduration/
  11317. * Fade in and out over a second
  11318. *
  11319. * @type {number}
  11320. * @default 100
  11321. * @since 1.2.0
  11322. * @apioption loading.showDuration
  11323. */
  11324. /**
  11325. * CSS styles for the loading label `span`.
  11326. *
  11327. * @see In styled mode, the loading label is styled with the
  11328. * `.highcharts-loading-inner` class.
  11329. *
  11330. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  11331. * Vertically centered
  11332. * @sample {highstock} stock/loading/general/
  11333. * Label styles
  11334. *
  11335. * @type {Highcharts.CSSObject}
  11336. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  11337. * @since 1.2.0
  11338. */
  11339. labelStyle: {
  11340. /**
  11341. * @ignore
  11342. */
  11343. fontWeight: 'bold',
  11344. /**
  11345. * @ignore
  11346. */
  11347. position: 'relative',
  11348. /**
  11349. * @ignore
  11350. */
  11351. top: '45%'
  11352. },
  11353. /**
  11354. * CSS styles for the loading screen that covers the plot area.
  11355. *
  11356. * In styled mode, the loading label is styled with the
  11357. * `.highcharts-loading` class.
  11358. *
  11359. * @sample {highcharts|highmaps} highcharts/loading/style/
  11360. * Gray plot area, white text
  11361. * @sample {highstock} stock/loading/general/
  11362. * Gray plot area, white text
  11363. *
  11364. * @type {Highcharts.CSSObject}
  11365. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  11366. * @since 1.2.0
  11367. */
  11368. style: {
  11369. /**
  11370. * @ignore
  11371. */
  11372. position: 'absolute',
  11373. /**
  11374. * @ignore
  11375. */
  11376. backgroundColor: '#ffffff',
  11377. /**
  11378. * @ignore
  11379. */
  11380. opacity: 0.5,
  11381. /**
  11382. * @ignore
  11383. */
  11384. textAlign: 'center'
  11385. }
  11386. },
  11387. /**
  11388. * Options for the tooltip that appears when the user hovers over a
  11389. * series or point.
  11390. *
  11391. * @declare Highcharts.TooltipOptions
  11392. */
  11393. tooltip: {
  11394. /**
  11395. * The color of the tooltip border. When `undefined`, the border takes
  11396. * the color of the corresponding series or point.
  11397. *
  11398. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11399. * Follow series by default
  11400. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  11401. * Black border
  11402. * @sample {highstock} stock/tooltip/general/
  11403. * Styled tooltip
  11404. * @sample {highmaps} maps/tooltip/background-border/
  11405. * Background and border demo
  11406. *
  11407. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11408. * @apioption tooltip.borderColor
  11409. */
  11410. /**
  11411. * A CSS class name to apply to the tooltip's container div,
  11412. * allowing unique CSS styling for each chart.
  11413. *
  11414. * @type {string}
  11415. * @apioption tooltip.className
  11416. */
  11417. /**
  11418. * Since 4.1, the crosshair definitions are moved to the Axis object
  11419. * in order for a better separation from the tooltip. See
  11420. * [xAxis.crosshair](#xAxis.crosshair).
  11421. *
  11422. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  11423. * Enable a crosshair for the x value
  11424. *
  11425. * @deprecated
  11426. *
  11427. * @type {*}
  11428. * @default true
  11429. * @apioption tooltip.crosshairs
  11430. */
  11431. /**
  11432. * Distance from point to tooltip in pixels.
  11433. *
  11434. * @type {number}
  11435. * @default 16
  11436. * @apioption tooltip.distance
  11437. */
  11438. /**
  11439. * Whether the tooltip should follow the mouse as it moves across
  11440. * columns, pie slices and other point types with an extent.
  11441. * By default it behaves this way for pie, polygon, map, sankey
  11442. * and wordcloud series by override in the `plotOptions`
  11443. * for those series types.
  11444. *
  11445. * For touch moves to behave the same way, [followTouchMove](
  11446. * #tooltip.followTouchMove) must be `true` also.
  11447. *
  11448. * @type {boolean}
  11449. * @default {highcharts} false
  11450. * @default {highstock} false
  11451. * @default {highmaps} true
  11452. * @since 3.0
  11453. * @apioption tooltip.followPointer
  11454. */
  11455. /**
  11456. * Whether the tooltip should update as the finger moves on a touch
  11457. * device. If this is `true` and [chart.panning](#chart.panning) is
  11458. * set,`followTouchMove` will take over one-finger touches, so the user
  11459. * needs to use two fingers for zooming and panning.
  11460. *
  11461. * Note the difference to [followPointer](#tooltip.followPointer) that
  11462. * only defines the _position_ of the tooltip. If `followPointer` is
  11463. * false in for example a column series, the tooltip will show above or
  11464. * below the column, but as `followTouchMove` is true, the tooltip will
  11465. * jump from column to column as the user swipes across the plot area.
  11466. *
  11467. * @type {boolean}
  11468. * @default {highcharts} true
  11469. * @default {highstock} true
  11470. * @default {highmaps} false
  11471. * @since 3.0.1
  11472. * @apioption tooltip.followTouchMove
  11473. */
  11474. /**
  11475. * Callback function to format the text of the tooltip from scratch. In
  11476. * case of single or [shared](#tooltip.shared) tooltips, a string should
  11477. * be returned. In case of [split](#tooltip.split) tooltips, it should
  11478. * return an array where the first item is the header, and subsequent
  11479. * items are mapped to the points. Return `false` to disable tooltip for
  11480. * a specific point on series.
  11481. *
  11482. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  11483. * the tooltip is parsed and converted to SVG, therefore this isn't a
  11484. * complete HTML renderer. The following HTML tags are supported: `b`,
  11485. * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
  11486. * attribute, but only text-related CSS, that is shared with SVG, is
  11487. * handled.
  11488. *
  11489. * The available data in the formatter differ a bit depending on whether
  11490. * the tooltip is shared or split, or belongs to a single point. In a
  11491. * shared/split tooltip, all properties except `x`, which is common for
  11492. * all points, are kept in an array, `this.points`.
  11493. *
  11494. * Available data are:
  11495. *
  11496. * - **this.percentage (not shared) /**
  11497. * **this.points[i].percentage (shared)**:
  11498. * Stacked series and pies only. The point's percentage of the total.
  11499. *
  11500. * - **this.point (not shared) / this.points[i].point (shared)**:
  11501. * The point object. The point name, if defined, is available through
  11502. * `this.point.name`.
  11503. *
  11504. * - **this.points**:
  11505. * In a shared tooltip, this is an array containing all other
  11506. * properties for each point.
  11507. *
  11508. * - **this.series (not shared) / this.points[i].series (shared)**:
  11509. * The series object. The series name is available through
  11510. * `this.series.name`.
  11511. *
  11512. * - **this.total (not shared) / this.points[i].total (shared)**:
  11513. * Stacked series only. The total value at this point's x value.
  11514. *
  11515. * - **this.x**:
  11516. * The x value. This property is the same regardless of the tooltip
  11517. * being shared or not.
  11518. *
  11519. * - **this.y (not shared) / this.points[i].y (shared)**:
  11520. * The y value.
  11521. *
  11522. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  11523. * Simple string formatting
  11524. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  11525. * Formatting with shared tooltip
  11526. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  11527. * Formatting with split tooltip
  11528. * @sample highcharts/tooltip/formatter-conditional-default/
  11529. * Extending default formatter
  11530. * @sample {highstock} stock/tooltip/formatter/
  11531. * Formatting with shared tooltip
  11532. * @sample {highmaps} maps/tooltip/formatter/
  11533. * String formatting
  11534. *
  11535. * @type {Highcharts.TooltipFormatterCallbackFunction}
  11536. * @apioption tooltip.formatter
  11537. */
  11538. /**
  11539. * Callback function to format the text of the tooltip for
  11540. * visible null points.
  11541. * Works analogously to [formatter](#tooltip.formatter).
  11542. *
  11543. * @sample highcharts/plotoptions/series-nullformat
  11544. * Format data label and tooltip for null point.
  11545. *
  11546. * @type {Highcharts.TooltipFormatterCallbackFunction}
  11547. * @apioption tooltip.nullFormatter
  11548. */
  11549. /**
  11550. * The number of milliseconds to wait until the tooltip is hidden when
  11551. * mouse out from a point or chart.
  11552. *
  11553. * @type {number}
  11554. * @default 500
  11555. * @since 3.0
  11556. * @apioption tooltip.hideDelay
  11557. */
  11558. /**
  11559. * Whether to allow the tooltip to render outside the chart's SVG
  11560. * element box. By default (`false`), the tooltip is rendered within the
  11561. * chart's SVG element, which results in the tooltip being aligned
  11562. * inside the chart area. For small charts, this may result in clipping
  11563. * or overlapping. When `true`, a separate SVG element is created and
  11564. * overlaid on the page, allowing the tooltip to be aligned inside the
  11565. * page itself.
  11566. *
  11567. * Defaults to `true` if `chart.scrollablePlotArea` is activated,
  11568. * otherwise `false`.
  11569. *
  11570. * @sample highcharts/tooltip/outside
  11571. * Small charts with tooltips outside
  11572. *
  11573. * @type {boolean|undefined}
  11574. * @default undefined
  11575. * @since 6.1.1
  11576. * @apioption tooltip.outside
  11577. */
  11578. /**
  11579. * A callback function for formatting the HTML output for a single point
  11580. * in the tooltip. Like the `pointFormat` string, but with more
  11581. * flexibility.
  11582. *
  11583. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  11584. * @since 4.1.0
  11585. * @context Highcharts.Point
  11586. * @apioption tooltip.pointFormatter
  11587. */
  11588. /**
  11589. * A callback function to place the tooltip in a default position. The
  11590. * callback receives three parameters: `labelWidth`, `labelHeight` and
  11591. * `point`, where point contains values for `plotX` and `plotY` telling
  11592. * where the reference point is in the plot area. Add `chart.plotLeft`
  11593. * and `chart.plotTop` to get the full coordinates.
  11594. *
  11595. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  11596. * positioner is called for each of the boxes separately, including
  11597. * xAxis header. xAxis header is not a point, instead `point` argument
  11598. * contains info:
  11599. * `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  11600. *
  11601. *
  11602. * The return should be an object containing x and y values, for example
  11603. * `{ x: 100, y: 100 }`.
  11604. *
  11605. * @sample {highcharts} highcharts/tooltip/positioner/
  11606. * A fixed tooltip position
  11607. * @sample {highstock} stock/tooltip/positioner/
  11608. * A fixed tooltip position on top of the chart
  11609. * @sample {highmaps} maps/tooltip/positioner/
  11610. * A fixed tooltip position
  11611. * @sample {highstock} stock/tooltip/split-positioner/
  11612. * Split tooltip with fixed positions
  11613. *
  11614. * @type {Highcharts.TooltipPositionerCallbackFunction}
  11615. * @since 2.2.4
  11616. * @apioption tooltip.positioner
  11617. */
  11618. /**
  11619. * The name of a symbol to use for the border around the tooltip. Can
  11620. * be one of: `"callout"`, `"circle"`, or `"square"`. When
  11621. * [tooltip.split](#tooltip.split)
  11622. * option is enabled, shape is applied to all boxes except header, which
  11623. * is controlled by
  11624. * [tooltip.headerShape](#tooltip.headerShape).
  11625. *
  11626. * Custom callbacks for symbol path generation can also be added to
  11627. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  11628. * [series.marker.symbol](plotOptions.line.marker.symbol).
  11629. *
  11630. * @type {Highcharts.TooltipShapeValue}
  11631. * @default callout
  11632. * @since 4.0
  11633. * @apioption tooltip.shape
  11634. */
  11635. /**
  11636. * The name of a symbol to use for the border around the tooltip
  11637. * header. Applies only when [tooltip.split](#tooltip.split) is
  11638. * enabled.
  11639. *
  11640. * Custom callbacks for symbol path generation can also be added to
  11641. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  11642. * [series.marker.symbol](plotOptions.line.marker.symbol).
  11643. *
  11644. * @see [tooltip.shape](#tooltip.shape)
  11645. *
  11646. * @sample {highstock} stock/tooltip/split-positioner/
  11647. * Different shapes for header and split boxes
  11648. *
  11649. * @type {Highcharts.TooltipShapeValue}
  11650. * @default callout
  11651. * @validvalue ["callout", "square"]
  11652. * @since 7.0
  11653. * @apioption tooltip.headerShape
  11654. */
  11655. /**
  11656. * When the tooltip is shared, the entire plot area will capture mouse
  11657. * movement or touch events. Tooltip texts for series types with ordered
  11658. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  11659. * This is recommended for single series charts and for tablet/mobile
  11660. * optimized charts.
  11661. *
  11662. * See also [tooltip.split](#tooltip.split), that is better suited for
  11663. * charts with many series, especially line-type series. The
  11664. * `tooltip.split` option takes precedence over `tooltip.shared`.
  11665. *
  11666. * @sample {highcharts} highcharts/tooltip/shared-false/
  11667. * False by default
  11668. * @sample {highcharts} highcharts/tooltip/shared-true/
  11669. * True
  11670. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  11671. * True with x axis crosshair
  11672. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  11673. * True with mixed series types
  11674. *
  11675. * @type {boolean}
  11676. * @default false
  11677. * @since 2.1
  11678. * @product highcharts highstock
  11679. * @apioption tooltip.shared
  11680. */
  11681. /**
  11682. * Split the tooltip into one label per series, with the header close
  11683. * to the axis. This is recommended over [shared](#tooltip.shared)
  11684. * tooltips for charts with multiple line series, generally making them
  11685. * easier to read. This option takes precedence over `tooltip.shared`.
  11686. *
  11687. * @productdesc {highstock} In Highstock, tooltips are split by default
  11688. * since v6.0.0. Stock charts typically contain multi-dimension points
  11689. * and multiple panes, making split tooltips the preferred layout over
  11690. * the previous `shared` tooltip.
  11691. *
  11692. * @sample highcharts/tooltip/split/
  11693. * Split tooltip
  11694. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  11695. * Split tooltip and custom formatter callback
  11696. *
  11697. * @type {boolean}
  11698. * @default {highcharts} false
  11699. * @default {highstock} true
  11700. * @since 5.0.0
  11701. * @product highcharts highstock
  11702. * @apioption tooltip.split
  11703. */
  11704. /**
  11705. * Use HTML to render the contents of the tooltip instead of SVG. Using
  11706. * HTML allows advanced formatting like tables and images in the
  11707. * tooltip. It is also recommended for rtl languages as it works around
  11708. * rtl bugs in early Firefox.
  11709. *
  11710. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  11711. * A table for value alignment
  11712. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  11713. * Full HTML tooltip
  11714. * @sample {highmaps} maps/tooltip/usehtml/
  11715. * Pure HTML tooltip
  11716. *
  11717. * @type {boolean}
  11718. * @default false
  11719. * @since 2.2
  11720. * @apioption tooltip.useHTML
  11721. */
  11722. /**
  11723. * How many decimals to show in each series' y value. This is
  11724. * overridable in each series' tooltip options object. The default is to
  11725. * preserve all decimals.
  11726. *
  11727. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11728. * Set decimals, prefix and suffix for the value
  11729. * @sample {highmaps} maps/tooltip/valuedecimals/
  11730. * Set decimals, prefix and suffix for the value
  11731. *
  11732. * @type {number}
  11733. * @since 2.2
  11734. * @apioption tooltip.valueDecimals
  11735. */
  11736. /**
  11737. * A string to prepend to each series' y value. Overridable in each
  11738. * series' tooltip options object.
  11739. *
  11740. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11741. * Set decimals, prefix and suffix for the value
  11742. * @sample {highmaps} maps/tooltip/valuedecimals/
  11743. * Set decimals, prefix and suffix for the value
  11744. *
  11745. * @type {string}
  11746. * @since 2.2
  11747. * @apioption tooltip.valuePrefix
  11748. */
  11749. /**
  11750. * A string to append to each series' y value. Overridable in each
  11751. * series' tooltip options object.
  11752. *
  11753. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11754. * Set decimals, prefix and suffix for the value
  11755. * @sample {highmaps} maps/tooltip/valuedecimals/
  11756. * Set decimals, prefix and suffix for the value
  11757. *
  11758. * @type {string}
  11759. * @since 2.2
  11760. * @apioption tooltip.valueSuffix
  11761. */
  11762. /**
  11763. * The format for the date in the tooltip header if the X axis is a
  11764. * datetime axis. The default is a best guess based on the smallest
  11765. * distance between points in the chart.
  11766. *
  11767. * @sample {highcharts} highcharts/tooltip/xdateformat/
  11768. * A different format
  11769. *
  11770. * @type {string}
  11771. * @product highcharts highstock gantt
  11772. * @apioption tooltip.xDateFormat
  11773. */
  11774. /**
  11775. * How many decimals to show for the `point.change` value when the
  11776. * `series.compare` option is set. This is overridable in each series'
  11777. * tooltip options object. The default is to preserve all decimals.
  11778. *
  11779. * @type {number}
  11780. * @since 1.0.1
  11781. * @product highstock
  11782. * @apioption tooltip.changeDecimals
  11783. */
  11784. /**
  11785. * Enable or disable the tooltip.
  11786. *
  11787. * @sample {highcharts} highcharts/tooltip/enabled/
  11788. * Disabled
  11789. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  11790. * Disable tooltip and show values on chart instead
  11791. */
  11792. enabled: true,
  11793. /**
  11794. * Enable or disable animation of the tooltip.
  11795. *
  11796. * @type {boolean}
  11797. * @default true
  11798. * @since 2.3.0
  11799. */
  11800. animation: svg,
  11801. /**
  11802. * The radius of the rounded border corners.
  11803. *
  11804. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11805. * 5px by default
  11806. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  11807. * Square borders
  11808. * @sample {highmaps} maps/tooltip/background-border/
  11809. * Background and border demo
  11810. */
  11811. borderRadius: 3,
  11812. /**
  11813. * For series on a datetime axes, the date format in the tooltip's
  11814. * header will by default be guessed based on the closest data points.
  11815. * This member gives the default string representations used for
  11816. * each unit. For an overview of the replacement codes, see
  11817. * [dateFormat](/class-reference/Highcharts#dateFormat).
  11818. *
  11819. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  11820. *
  11821. * @type {Highcharts.Dictionary<string>}
  11822. * @product highcharts highstock gantt
  11823. */
  11824. dateTimeLabelFormats: {
  11825. /** @internal */
  11826. millisecond: '%A, %b %e, %H:%M:%S.%L',
  11827. /** @internal */
  11828. second: '%A, %b %e, %H:%M:%S',
  11829. /** @internal */
  11830. minute: '%A, %b %e, %H:%M',
  11831. /** @internal */
  11832. hour: '%A, %b %e, %H:%M',
  11833. /** @internal */
  11834. day: '%A, %b %e, %Y',
  11835. /** @internal */
  11836. week: 'Week from %A, %b %e, %Y',
  11837. /** @internal */
  11838. month: '%B %Y',
  11839. /** @internal */
  11840. year: '%Y'
  11841. },
  11842. /**
  11843. * A string to append to the tooltip format.
  11844. *
  11845. * @sample {highcharts} highcharts/tooltip/footerformat/
  11846. * A table for value alignment
  11847. * @sample {highmaps} maps/tooltip/format/
  11848. * Format demo
  11849. *
  11850. * @since 2.2
  11851. */
  11852. footerFormat: '',
  11853. /**
  11854. * Padding inside the tooltip, in pixels.
  11855. *
  11856. * @since 5.0.0
  11857. */
  11858. padding: 8,
  11859. /**
  11860. * Proximity snap for graphs or single points. It defaults to 10 for
  11861. * mouse-powered devices and 25 for touch devices.
  11862. *
  11863. * Note that in most cases the whole plot area captures the mouse
  11864. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  11865. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  11866. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  11867. * or [split](#tooltip.split).
  11868. *
  11869. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11870. * 10 px by default
  11871. * @sample {highcharts} highcharts/tooltip/snap-50/
  11872. * 50 px on graph
  11873. *
  11874. * @type {number}
  11875. * @default 10/25
  11876. * @since 1.2.0
  11877. * @product highcharts highstock
  11878. */
  11879. snap: isTouchDevice ? 25 : 10,
  11880. /**
  11881. * The HTML of the tooltip header line. Variables are enclosed by
  11882. * curly brackets. Available variables are `point.key`, `series.name`,
  11883. * `series.color` and other members from the `point` and `series`
  11884. * objects. The `point.key` variable contains the category name, x
  11885. * value or datetime string depending on the type of axis. For datetime
  11886. * axes, the `point.key` date format can be set using
  11887. * `tooltip.xDateFormat`.
  11888. *
  11889. * @sample {highcharts} highcharts/tooltip/footerformat/
  11890. * An HTML table in the tooltip
  11891. * @sample {highstock} highcharts/tooltip/footerformat/
  11892. * An HTML table in the tooltip
  11893. * @sample {highmaps} maps/tooltip/format/
  11894. * Format demo
  11895. *
  11896. * @type {string}
  11897. * @apioption tooltip.headerFormat
  11898. */
  11899. headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
  11900. /**
  11901. * The HTML of the null point's line in the tooltip. Works analogously
  11902. * to [pointFormat](#tooltip.pointFormat).
  11903. *
  11904. * @sample {highcharts} highcharts/plotoptions/series-nullformat
  11905. * Format data label and tooltip for null point.
  11906. *
  11907. * @type {string}
  11908. * @apioption tooltip.nullFormat
  11909. */
  11910. /**
  11911. * The HTML of the point's line in the tooltip. Variables are enclosed
  11912. * by curly brackets. Available variables are point.x, point.y, series.
  11913. * name and series.color and other properties on the same form.
  11914. * Furthermore, `point.y` can be extended by the `tooltip.valuePrefix`
  11915. * and `tooltip.valueSuffix` variables. This can also be overridden for
  11916. * each series, which makes it a good hook for displaying units.
  11917. *
  11918. * In styled mode, the dot is colored by a class name rather
  11919. * than the point color.
  11920. *
  11921. * @sample {highcharts} highcharts/tooltip/pointformat/
  11922. * A different point format with value suffix
  11923. * @sample {highmaps} maps/tooltip/format/
  11924. * Format demo
  11925. *
  11926. * @type {string}
  11927. * @since 2.2
  11928. * @apioption tooltip.pointFormat
  11929. */
  11930. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  11931. /**
  11932. * The background color or gradient for the tooltip.
  11933. *
  11934. * In styled mode, the stroke width is set in the
  11935. * `.highcharts-tooltip-box` class.
  11936. *
  11937. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  11938. * Yellowish background
  11939. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  11940. * Gradient
  11941. * @sample {highcharts} highcharts/css/tooltip-border-background/
  11942. * Tooltip in styled mode
  11943. * @sample {highstock} stock/tooltip/general/
  11944. * Custom tooltip
  11945. * @sample {highstock} highcharts/css/tooltip-border-background/
  11946. * Tooltip in styled mode
  11947. * @sample {highmaps} maps/tooltip/background-border/
  11948. * Background and border demo
  11949. * @sample {highmaps} highcharts/css/tooltip-border-background/
  11950. * Tooltip in styled mode
  11951. *
  11952. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11953. */
  11954. backgroundColor: color('#f7f7f7')
  11955. .setOpacity(0.85).get(),
  11956. /**
  11957. * The pixel width of the tooltip border.
  11958. *
  11959. * In styled mode, the stroke width is set in the
  11960. * `.highcharts-tooltip-box` class.
  11961. *
  11962. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11963. * 2px by default
  11964. * @sample {highcharts} highcharts/tooltip/borderwidth/
  11965. * No border (shadow only)
  11966. * @sample {highcharts} highcharts/css/tooltip-border-background/
  11967. * Tooltip in styled mode
  11968. * @sample {highstock} stock/tooltip/general/
  11969. * Custom tooltip
  11970. * @sample {highstock} highcharts/css/tooltip-border-background/
  11971. * Tooltip in styled mode
  11972. * @sample {highmaps} maps/tooltip/background-border/
  11973. * Background and border demo
  11974. * @sample {highmaps} highcharts/css/tooltip-border-background/
  11975. * Tooltip in styled mode
  11976. */
  11977. borderWidth: 1,
  11978. /**
  11979. * Whether to apply a drop shadow to the tooltip.
  11980. *
  11981. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11982. * True by default
  11983. * @sample {highcharts} highcharts/tooltip/shadow/
  11984. * False
  11985. * @sample {highmaps} maps/tooltip/positioner/
  11986. * Fixed tooltip position, border and shadow disabled
  11987. *
  11988. * @type {boolean|Highcharts.ShadowOptionsObject}
  11989. */
  11990. shadow: true,
  11991. /**
  11992. * CSS styles for the tooltip. The tooltip can also be styled through
  11993. * the CSS class `.highcharts-tooltip`.
  11994. *
  11995. * Note that the default `pointerEvents` style makes the tooltip ignore
  11996. * mouse events, so in order to use clickable tooltips, this value must
  11997. * be set to `auto`.
  11998. *
  11999. * @sample {highcharts} highcharts/tooltip/style/
  12000. * Greater padding, bold text
  12001. *
  12002. * @type {Highcharts.CSSObject}
  12003. */
  12004. style: {
  12005. /** @internal */
  12006. color: '#333333',
  12007. /** @internal */
  12008. cursor: 'default',
  12009. /** @internal */
  12010. fontSize: '12px',
  12011. /** @internal */
  12012. pointerEvents: 'none',
  12013. /** @internal */
  12014. whiteSpace: 'nowrap'
  12015. }
  12016. },
  12017. /**
  12018. * Highchart by default puts a credits label in the lower right corner
  12019. * of the chart. This can be changed using these options.
  12020. */
  12021. credits: {
  12022. /**
  12023. * Credits for map source to be concatenated with conventional credit
  12024. * text. By default this is a format string that collects copyright
  12025. * information from the map if available.
  12026. *
  12027. * @see [mapTextFull](#credits.mapTextFull)
  12028. * @see [text](#credits.text)
  12029. *
  12030. * @type {string}
  12031. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  12032. * @since 4.2.2
  12033. * @product highmaps
  12034. * @apioption credits.mapText
  12035. */
  12036. /**
  12037. * Detailed credits for map source to be displayed on hover of credits
  12038. * text. By default this is a format string that collects copyright
  12039. * information from the map if available.
  12040. *
  12041. * @see [mapText](#credits.mapText)
  12042. * @see [text](#credits.text)
  12043. *
  12044. * @type {string}
  12045. * @default {geojson.copyright}
  12046. * @since 4.2.2
  12047. * @product highmaps
  12048. * @apioption credits.mapTextFull
  12049. */
  12050. /**
  12051. * Whether to show the credits text.
  12052. *
  12053. * @sample {highcharts} highcharts/credits/enabled-false/
  12054. * Credits disabled
  12055. * @sample {highstock} stock/credits/enabled/
  12056. * Credits disabled
  12057. * @sample {highmaps} maps/credits/enabled-false/
  12058. * Credits disabled
  12059. */
  12060. enabled: true,
  12061. /**
  12062. * The URL for the credits label.
  12063. *
  12064. * @sample {highcharts} highcharts/credits/href/
  12065. * Custom URL and text
  12066. * @sample {highmaps} maps/credits/customized/
  12067. * Custom URL and text
  12068. */
  12069. href: 'https://www.highcharts.com?credits',
  12070. /**
  12071. * Position configuration for the credits label.
  12072. *
  12073. * @sample {highcharts} highcharts/credits/position-left/
  12074. * Left aligned
  12075. * @sample {highcharts} highcharts/credits/position-left/
  12076. * Left aligned
  12077. * @sample {highmaps} maps/credits/customized/
  12078. * Left aligned
  12079. * @sample {highmaps} maps/credits/customized/
  12080. * Left aligned
  12081. *
  12082. * @type {Highcharts.AlignObject}
  12083. * @since 2.1
  12084. */
  12085. position: {
  12086. /** @internal */
  12087. align: 'right',
  12088. /** @internal */
  12089. x: -10,
  12090. /** @internal */
  12091. verticalAlign: 'bottom',
  12092. /** @internal */
  12093. y: -5
  12094. },
  12095. /**
  12096. * CSS styles for the credits label.
  12097. *
  12098. * @see In styled mode, credits styles can be set with the
  12099. * `.highcharts-credits` class.
  12100. *
  12101. * @type {Highcharts.CSSObject}
  12102. */
  12103. style: {
  12104. /** @internal */
  12105. cursor: 'pointer',
  12106. /** @internal */
  12107. color: '#999999',
  12108. /** @internal */
  12109. fontSize: '9px'
  12110. },
  12111. /**
  12112. * The text for the credits label.
  12113. *
  12114. * @productdesc {highmaps}
  12115. * If a map is loaded as GeoJSON, the text defaults to
  12116. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  12117. * `Highcharts.com`.
  12118. *
  12119. * @sample {highcharts} highcharts/credits/href/
  12120. * Custom URL and text
  12121. * @sample {highmaps} maps/credits/customized/
  12122. * Custom URL and text
  12123. */
  12124. text: 'Highcharts.com'
  12125. }
  12126. };
  12127. /**
  12128. * Merge the default options with custom options and return the new options
  12129. * structure. Commonly used for defining reusable templates.
  12130. *
  12131. * @sample highcharts/global/useutc-false Setting a global option
  12132. * @sample highcharts/members/setoptions Applying a global theme
  12133. *
  12134. * @function Highcharts.setOptions
  12135. *
  12136. * @param {Highcharts.Options} options
  12137. * The new custom chart options.
  12138. *
  12139. * @return {Highcharts.Options}
  12140. * Updated options.
  12141. */
  12142. H.setOptions = function (options) {
  12143. // Copy in the default options
  12144. H.defaultOptions = merge(true, H.defaultOptions, options);
  12145. // Update the time object
  12146. if (options.time || options.global) {
  12147. H.time.update(merge(H.defaultOptions.global, H.defaultOptions.time, options.global, options.time));
  12148. }
  12149. return H.defaultOptions;
  12150. };
  12151. /**
  12152. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  12153. * for outside modules wasn't enough because the setOptions method created a new
  12154. * object.
  12155. *
  12156. * @function Highcharts.getOptions
  12157. *
  12158. * @return {Highcharts.Options}
  12159. */
  12160. H.getOptions = function () {
  12161. return H.defaultOptions;
  12162. };
  12163. // Series defaults
  12164. H.defaultPlotOptions = H.defaultOptions.plotOptions;
  12165. /**
  12166. * Global `Time` object with default options. Since v6.0.5, time settings can be
  12167. * applied individually for each chart. If no individual settings apply, this
  12168. * `Time` object is shared by all instances.
  12169. *
  12170. * @name Highcharts.time
  12171. * @type {Highcharts.Time}
  12172. */
  12173. H.time = new H.Time(merge(H.defaultOptions.global, H.defaultOptions.time));
  12174. /**
  12175. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  12176. * human readable date string. The format is a subset of the formats for PHP's
  12177. * [strftime](http://www.php.net/manual/en/function.strftime.php) function.
  12178. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  12179. *
  12180. * Since v6.0.5, all internal dates are formatted through the
  12181. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  12182. * The `Highcharts.dateFormat` function only reflects global time settings set
  12183. * with `setOptions`.
  12184. *
  12185. * @function Highcharts.dateFormat
  12186. *
  12187. * @param {string} format
  12188. * The desired format where various time representations are prefixed
  12189. * with `%`.
  12190. *
  12191. * @param {number} timestamp
  12192. * The JavaScript timestamp.
  12193. *
  12194. * @param {boolean} [capitalize=false]
  12195. * Upper case first letter in the return.
  12196. *
  12197. * @return {string}
  12198. * The formatted date.
  12199. */
  12200. H.dateFormat = function (format, timestamp, capitalize) {
  12201. return H.time.dateFormat(format, timestamp, capitalize);
  12202. };
  12203. /* eslint-disable spaced-comment */
  12204. '';
  12205. });
  12206. _registerModule(_modules, 'parts/Tick.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  12207. /* *
  12208. *
  12209. * (c) 2010-2019 Torstein Honsi
  12210. *
  12211. * License: www.highcharts.com/license
  12212. *
  12213. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  12214. *
  12215. * */
  12216. /**
  12217. * Optional parameters for the tick.
  12218. * @private
  12219. * @interface Highcharts.TickParametersObject
  12220. */ /**
  12221. * Set category for the tick.
  12222. * @name Highcharts.TickParametersObject#category
  12223. * @type {string|undefined}
  12224. */ /**
  12225. * @name Highcharts.TickParametersObject#options
  12226. * @type {Highcharts.Dictionary<any>|undefined}
  12227. */ /**
  12228. * Set tickmarkOffset for the tick.
  12229. * @name Highcharts.TickParametersObject#tickmarkOffset
  12230. * @type {number|undefined}
  12231. */
  12232. var clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, extend = U.extend, isNumber = U.isNumber, objectEach = U.objectEach, pick = U.pick;
  12233. var fireEvent = H.fireEvent, merge = H.merge, deg2rad = H.deg2rad;
  12234. /* eslint-disable no-invalid-this, valid-jsdoc */
  12235. /**
  12236. * The Tick class.
  12237. *
  12238. * @class
  12239. * @name Highcharts.Tick
  12240. *
  12241. * @param {Highcharts.Axis} axis
  12242. * The axis of the tick.
  12243. *
  12244. * @param {number} pos
  12245. * The position of the tick on the axis in terms of axis values.
  12246. *
  12247. * @param {string} [type]
  12248. * The type of tick, either 'minor' or an empty string
  12249. *
  12250. * @param {boolean} [noLabel=false]
  12251. * Whether to disable the label or not. Defaults to false.
  12252. *
  12253. * @param {object} [parameters]
  12254. * Optional parameters for the tick.
  12255. */
  12256. H.Tick = function (axis, pos, type, noLabel, parameters) {
  12257. /**
  12258. * The related axis of the tick.
  12259. * @name Highcharts.Tick#axis
  12260. * @type {Highcharts.Axis}
  12261. */
  12262. this.axis = axis;
  12263. /**
  12264. * The logical position of the tick on the axis in terms of axis values.
  12265. * @name Highcharts.Tick#pos
  12266. * @type {number}
  12267. */
  12268. this.pos = pos;
  12269. /**
  12270. * The tick type, which can be `"minor"`, or an empty string.
  12271. * @name Highcharts.Tick#type
  12272. * @type {string}
  12273. */
  12274. this.type = type || '';
  12275. this.isNew = true;
  12276. this.isNewLabel = true;
  12277. this.parameters = parameters || {};
  12278. /**
  12279. * The mark offset of the tick on the axis. Usually `undefined`, numeric for
  12280. * grid axes.
  12281. * @name Highcharts.Tick#tickmarkOffset
  12282. * @type {number|undefined}
  12283. */
  12284. this.tickmarkOffset = this.parameters.tickmarkOffset;
  12285. this.options = this.parameters.options;
  12286. if (!type && !noLabel) {
  12287. this.addLabel();
  12288. }
  12289. };
  12290. /** @lends Highcharts.Tick.prototype */
  12291. H.Tick.prototype = {
  12292. /**
  12293. * Write the tick label.
  12294. *
  12295. * @private
  12296. * @function Highcharts.Tick#addLabel
  12297. * @return {void}
  12298. */
  12299. addLabel: function () {
  12300. var tick = this, axis = tick.axis, options = axis.options, chart = axis.chart, categories = axis.categories, 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 ?
  12301. pick(categories[pos], names[pos], pos) :
  12302. pos), label = tick.label, animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
  12303. axis.tickInterval === 1, tickPositionInfo = tickPositions.info, dateTimeLabelFormat, dateTimeLabelFormats, i, list;
  12304. // Set the datetime label format. If a higher rank is set for this
  12305. // position, use that. If not, use the general format.
  12306. if (axis.isDatetimeAxis && tickPositionInfo) {
  12307. dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
  12308. tickPositionInfo.higherRanks[pos]) ||
  12309. tickPositionInfo.unitName]);
  12310. dateTimeLabelFormat = dateTimeLabelFormats.main;
  12311. }
  12312. // set properties for access in render method
  12313. /**
  12314. * True if the tick is the first one on the axis.
  12315. * @name Highcharts.Tick#isFirst
  12316. * @readonly
  12317. * @type {boolean|undefined}
  12318. */
  12319. tick.isFirst = isFirst;
  12320. /**
  12321. * True if the tick is the last one on the axis.
  12322. * @name Highcharts.Tick#isLast
  12323. * @readonly
  12324. * @type {boolean|undefined}
  12325. */
  12326. tick.isLast = isLast;
  12327. // Get the string
  12328. tick.formatCtx = {
  12329. axis: axis,
  12330. chart: chart,
  12331. isFirst: isFirst,
  12332. isLast: isLast,
  12333. dateTimeLabelFormat: dateTimeLabelFormat,
  12334. tickPositionInfo: tickPositionInfo,
  12335. value: axis.isLog ? correctFloat(axis.lin2log(value)) : value,
  12336. pos: pos
  12337. };
  12338. str = axis.labelFormatter.call(tick.formatCtx, this.formatCtx);
  12339. // Set up conditional formatting based on the format list if existing.
  12340. list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  12341. if (list) {
  12342. tick.shortenLabel = function () {
  12343. for (i = 0; i < list.length; i++) {
  12344. label.attr({
  12345. text: axis.labelFormatter.call(extend(tick.formatCtx, { dateTimeLabelFormat: list[i] }))
  12346. });
  12347. if (label.getBBox().width <
  12348. axis.getSlotWidth(tick) - 2 *
  12349. pick(labelOptions.padding, 5)) {
  12350. return;
  12351. }
  12352. }
  12353. label.attr({
  12354. text: ''
  12355. });
  12356. };
  12357. }
  12358. // Call only after first render
  12359. if (animateLabels && axis._addedPlotLB && axis.isXAxis) {
  12360. tick.moveLabel(str, labelOptions);
  12361. }
  12362. // First call
  12363. if (!defined(label) && !tick.movedLabel) {
  12364. tick.label = label = tick.createLabel({ x: 0, y: 0 }, str, labelOptions);
  12365. // Base value to detect change for new calls to getBBox
  12366. tick.rotation = 0;
  12367. // update
  12368. }
  12369. else if (label && label.textStr !== str && !animateLabels) {
  12370. // When resetting text, also reset the width if dynamically set
  12371. // (#8809)
  12372. if (label.textWidth &&
  12373. !(labelOptions.style && labelOptions.style.width) &&
  12374. !label.styles.width) {
  12375. label.css({ width: null });
  12376. }
  12377. label.attr({ text: str });
  12378. label.textPxLength = label.getBBox().width;
  12379. }
  12380. },
  12381. /**
  12382. * Try to replace the label if the same one already exists.
  12383. *
  12384. * @private
  12385. * @function Highcharts.Tick#moveLabel
  12386. * @param {string} str
  12387. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  12388. *
  12389. * @return {void}
  12390. */
  12391. moveLabel: function (str, labelOptions) {
  12392. var tick = this, label = tick.label, moved = false, xAxis = tick.axis, chart = xAxis.chart, labelPos, reversed = xAxis.reversed, inverted = chart.inverted, xPos, yPos;
  12393. if (label && label.textStr === str) {
  12394. tick.movedLabel = label;
  12395. moved = true;
  12396. delete tick.label;
  12397. }
  12398. else { // Find a label with the same string
  12399. objectEach(xAxis.ticks, function (currentTick) {
  12400. if (!moved &&
  12401. !currentTick.isNew &&
  12402. currentTick !== tick &&
  12403. currentTick.label &&
  12404. currentTick.label.textStr === str) {
  12405. tick.movedLabel = currentTick.label;
  12406. moved = true;
  12407. currentTick.labelPos = tick.movedLabel.xy;
  12408. delete currentTick.label;
  12409. }
  12410. });
  12411. }
  12412. // Create new label if the actual one is moved
  12413. if (!moved && (tick.labelPos || label)) {
  12414. labelPos = tick.labelPos || label.xy;
  12415. xPos = inverted ?
  12416. labelPos.x : (reversed ? 0 : xAxis.width + xAxis.left);
  12417. yPos = inverted ?
  12418. (reversed ? (xAxis.width + xAxis.left) : 0) : labelPos.y;
  12419. tick.movedLabel = tick.createLabel({ x: xPos, y: yPos }, str, labelOptions);
  12420. if (tick.movedLabel) {
  12421. tick.movedLabel.attr({ opacity: 0 });
  12422. }
  12423. }
  12424. },
  12425. /**
  12426. * Render and return the label of the tick.
  12427. *
  12428. * @private
  12429. * @function Highcharts.Tick#createLabel
  12430. * @param {Highcharts.PositionObject} xy
  12431. * @param {string} str
  12432. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  12433. * @return {Highcharts.SVGElement|undefined}
  12434. */
  12435. createLabel: function (xy, str, labelOptions) {
  12436. var axis = this.axis, chart = axis.chart, label = defined(str) && labelOptions.enabled ?
  12437. chart.renderer
  12438. .text(str, xy.x, xy.y, labelOptions.useHTML)
  12439. .add(axis.labelGroup) :
  12440. null;
  12441. // Un-rotated length
  12442. if (label) {
  12443. // Without position absolute, IE export sometimes is wrong
  12444. if (!chart.styledMode) {
  12445. label.css(merge(labelOptions.style));
  12446. }
  12447. label.textPxLength = label.getBBox().width;
  12448. }
  12449. return label;
  12450. },
  12451. /**
  12452. * Replace labels with the moved ones to perform animation. Additionally
  12453. * destroy unused labels.
  12454. *
  12455. * @private
  12456. * @function Highcharts.Tick#replaceMovedLabel
  12457. * @return {void}
  12458. */
  12459. replaceMovedLabel: function () {
  12460. var tick = this, label = tick.label, axis = tick.axis, reversed = axis.reversed, chart = tick.axis.chart, inverted = chart.inverted, x, y;
  12461. // Animate and destroy
  12462. if (label && !tick.isNew) {
  12463. x = inverted ? label.xy.x : (reversed ? axis.left : axis.width + axis.left);
  12464. y = inverted ?
  12465. (reversed ? axis.width + axis.top : axis.top) :
  12466. label.xy.y;
  12467. label.animate({ x: x, y: y, opacity: 0 }, void 0, label.destroy);
  12468. delete tick.label;
  12469. }
  12470. axis.isDirty = true;
  12471. tick.label = tick.movedLabel;
  12472. delete tick.movedLabel;
  12473. },
  12474. /**
  12475. * Get the offset height or width of the label
  12476. *
  12477. * @private
  12478. * @function Highcharts.Tick#getLabelSize
  12479. * @return {number}
  12480. */
  12481. getLabelSize: function () {
  12482. return this.label ?
  12483. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  12484. 0;
  12485. },
  12486. /**
  12487. * Handle the label overflow by adjusting the labels to the left and right
  12488. * edge, or hide them if they collide into the neighbour label.
  12489. *
  12490. * @private
  12491. * @function Highcharts.Tick#handleOverflow
  12492. * @param {Highcharts.PositionObject} xy
  12493. * @return {void}
  12494. */
  12495. handleOverflow: function (xy) {
  12496. 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 = {
  12497. left: 0,
  12498. center: 0.5,
  12499. right: 1
  12500. }[axis.labelAlign || label.attr('align')], labelWidth = label.getBBox().width, slotWidth = axis.getSlotWidth(tick), modifiedSlotWidth = slotWidth, xCorrection = factor, goRight = 1, leftPos, rightPos, textWidth, css = {};
  12501. // Check if the label overshoots the chart spacing box. If it does, move
  12502. // it. If it now overshoots the slotWidth, add ellipsis.
  12503. if (!rotation &&
  12504. pick(labelOptions.overflow, 'justify') === 'justify') {
  12505. leftPos = pxPos - factor * labelWidth;
  12506. rightPos = pxPos + (1 - factor) * labelWidth;
  12507. if (leftPos < leftBound) {
  12508. modifiedSlotWidth =
  12509. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  12510. }
  12511. else if (rightPos > rightBound) {
  12512. modifiedSlotWidth =
  12513. rightBound - xy.x + modifiedSlotWidth * factor;
  12514. goRight = -1;
  12515. }
  12516. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  12517. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  12518. xy.x += (goRight *
  12519. (slotWidth -
  12520. modifiedSlotWidth -
  12521. xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
  12522. }
  12523. // If the label width exceeds the available space, set a text width
  12524. // to be picked up below. Also, if a width has been set before, we
  12525. // need to set a new one because the reported labelWidth will be
  12526. // limited by the box (#3938).
  12527. if (labelWidth > modifiedSlotWidth ||
  12528. (axis.autoRotation && (label.styles || {}).width)) {
  12529. textWidth = modifiedSlotWidth;
  12530. }
  12531. // Add ellipsis to prevent rotated labels to be clipped against the edge
  12532. // of the chart
  12533. }
  12534. else if (rotation < 0 &&
  12535. pxPos - factor * labelWidth < leftBound) {
  12536. textWidth = Math.round(pxPos / Math.cos(rotation * deg2rad) - leftBound);
  12537. }
  12538. else if (rotation > 0 &&
  12539. pxPos + factor * labelWidth > rightBound) {
  12540. textWidth = Math.round((chartWidth - pxPos) /
  12541. Math.cos(rotation * deg2rad));
  12542. }
  12543. if (textWidth) {
  12544. if (tick.shortenLabel) {
  12545. tick.shortenLabel();
  12546. }
  12547. else {
  12548. css.width = Math.floor(textWidth);
  12549. if (!(labelOptions.style || {}).textOverflow) {
  12550. css.textOverflow = 'ellipsis';
  12551. }
  12552. label.css(css);
  12553. }
  12554. }
  12555. },
  12556. /**
  12557. * Gets the x and y positions for ticks in terms of pixels.
  12558. *
  12559. * @private
  12560. * @function Highcharts.Tick#getPosition
  12561. *
  12562. * @param {boolean} horiz
  12563. * Whether the tick is on an horizontal axis or not.
  12564. *
  12565. * @param {number} tickPos
  12566. * Position of the tick.
  12567. *
  12568. * @param {number} tickmarkOffset
  12569. * Tickmark offset for all ticks.
  12570. *
  12571. * @param {boolean} [old]
  12572. * Whether the axis has changed or not.
  12573. *
  12574. * @return {Highcharts.PositionObject}
  12575. * The tick position.
  12576. *
  12577. * @fires Highcharts.Tick#event:afterGetPosition
  12578. */
  12579. getPosition: function (horiz, tickPos, tickmarkOffset, old) {
  12580. var axis = this.axis, chart = axis.chart, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, pos;
  12581. pos = {
  12582. x: horiz ?
  12583. correctFloat(axis.translate(tickPos + tickmarkOffset, null, null, old) +
  12584. axis.transB) :
  12585. (axis.left +
  12586. axis.offset +
  12587. (axis.opposite ?
  12588. (((old && chart.oldChartWidth) ||
  12589. chart.chartWidth) -
  12590. axis.right -
  12591. axis.left) :
  12592. 0)),
  12593. y: horiz ?
  12594. (cHeight -
  12595. axis.bottom +
  12596. axis.offset -
  12597. (axis.opposite ? axis.height : 0)) :
  12598. correctFloat(cHeight -
  12599. axis.translate(tickPos + tickmarkOffset, null, null, old) -
  12600. axis.transB)
  12601. };
  12602. // Chrome workaround for #10516
  12603. pos.y = clamp(pos.y, -1e5, 1e5);
  12604. fireEvent(this, 'afterGetPosition', { pos: pos });
  12605. return pos;
  12606. },
  12607. /**
  12608. * Get the x, y position of the tick label
  12609. *
  12610. * @private
  12611. * @return {Highcharts.PositionObject}
  12612. */
  12613. getLabelPosition: function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  12614. var axis = this.axis, transA = axis.transA, reversed = ( // #7911
  12615. axis.isLinked && axis.linkedParent ?
  12616. axis.linkedParent.reversed :
  12617. axis.reversed), staggerLines = axis.staggerLines, rotCorr = axis.tickRotCorr || { x: 0, y: 0 }, yOffset = labelOptions.y,
  12618. // Adjust for label alignment if we use reserveSpace: true (#5286)
  12619. labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
  12620. -axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
  12621. 0), line, pos = {};
  12622. if (!defined(yOffset)) {
  12623. if (axis.side === 0) {
  12624. yOffset = label.rotation ? -8 : -label.getBBox().height;
  12625. }
  12626. else if (axis.side === 2) {
  12627. yOffset = rotCorr.y + 8;
  12628. }
  12629. else {
  12630. // #3140, #3140
  12631. yOffset = Math.cos(label.rotation * deg2rad) *
  12632. (rotCorr.y - label.getBBox(false, 0).height / 2);
  12633. }
  12634. }
  12635. x = x +
  12636. labelOptions.x +
  12637. labelOffsetCorrection +
  12638. rotCorr.x -
  12639. (tickmarkOffset && horiz ?
  12640. tickmarkOffset * transA * (reversed ? -1 : 1) :
  12641. 0);
  12642. y = y + yOffset - (tickmarkOffset && !horiz ?
  12643. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  12644. // Correct for staggered labels
  12645. if (staggerLines) {
  12646. line = (index / (step || 1) % staggerLines);
  12647. if (axis.opposite) {
  12648. line = staggerLines - line - 1;
  12649. }
  12650. y += line * (axis.labelOffset / staggerLines);
  12651. }
  12652. pos.x = x;
  12653. pos.y = Math.round(y);
  12654. fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
  12655. return pos;
  12656. },
  12657. /**
  12658. * Extendible method to return the path of the marker
  12659. *
  12660. * @private
  12661. *
  12662. */
  12663. getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) {
  12664. return renderer.crispLine([
  12665. 'M',
  12666. x,
  12667. y,
  12668. 'L',
  12669. x + (horiz ? 0 : -tickLength),
  12670. y + (horiz ? tickLength : 0)
  12671. ], tickWidth);
  12672. },
  12673. /**
  12674. * Renders the gridLine.
  12675. *
  12676. * @private
  12677. * @param {boolean} old Whether or not the tick is old
  12678. * @param {number} opacity The opacity of the grid line
  12679. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  12680. * @return {void}
  12681. */
  12682. renderGridLine: function (old, opacity, reverseCrisp) {
  12683. 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'];
  12684. if (!gridLine) {
  12685. if (!axis.chart.styledMode) {
  12686. attribs.stroke = gridLineColor;
  12687. attribs['stroke-width'] = gridLineWidth;
  12688. if (dashStyle) {
  12689. attribs.dashstyle = dashStyle;
  12690. }
  12691. }
  12692. if (!type) {
  12693. attribs.zIndex = 1;
  12694. }
  12695. if (old) {
  12696. opacity = 0;
  12697. }
  12698. /**
  12699. * The rendered grid line of the tick.
  12700. * @name Highcharts.Tick#gridLine
  12701. * @type {Highcharts.SVGElement|undefined}
  12702. */
  12703. tick.gridLine = gridLine = renderer.path()
  12704. .attr(attribs)
  12705. .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
  12706. .add(axis.gridGroup);
  12707. }
  12708. if (gridLine) {
  12709. gridLinePath = axis.getPlotLinePath({
  12710. value: pos + tickmarkOffset,
  12711. lineWidth: gridLine.strokeWidth() * reverseCrisp,
  12712. force: 'pass',
  12713. old: old
  12714. });
  12715. // If the parameter 'old' is set, the current call will be followed
  12716. // by another call, therefore do not do any animations this time
  12717. if (gridLinePath) {
  12718. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  12719. d: gridLinePath,
  12720. opacity: opacity
  12721. });
  12722. }
  12723. }
  12724. },
  12725. /**
  12726. * Renders the tick mark.
  12727. *
  12728. * @private
  12729. * @param {Highcharts.PositionObject} xy The position vector of the mark
  12730. * @param {number} opacity The opacity of the mark
  12731. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  12732. * @return {void}
  12733. */
  12734. renderMark: function (xy, opacity, reverseCrisp) {
  12735. 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
  12736. tickColor = options[tickPrefix + 'Color'];
  12737. if (tickSize) {
  12738. // negate the length
  12739. if (axis.opposite) {
  12740. tickSize[0] = -tickSize[0];
  12741. }
  12742. // First time, create it
  12743. if (isNewMark) {
  12744. /**
  12745. * The rendered mark of the tick.
  12746. * @name Highcharts.Tick#mark
  12747. * @type {Highcharts.SVGElement|undefined}
  12748. */
  12749. tick.mark = mark = renderer.path()
  12750. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  12751. .add(axis.axisGroup);
  12752. if (!axis.chart.styledMode) {
  12753. mark.attr({
  12754. stroke: tickColor,
  12755. 'stroke-width': tickWidth
  12756. });
  12757. }
  12758. }
  12759. mark[isNewMark ? 'attr' : 'animate']({
  12760. d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, axis.horiz, renderer),
  12761. opacity: opacity
  12762. });
  12763. }
  12764. },
  12765. /**
  12766. * Renders the tick label.
  12767. * Note: The label should already be created in init(), so it should only
  12768. * have to be moved into place.
  12769. *
  12770. * @private
  12771. * @param {Highcharts.PositionObject} xy The position vector of the label
  12772. * @param {boolean} old Whether or not the tick is old
  12773. * @param {number} opacity The opacity of the label
  12774. * @param {number} index The index of the tick
  12775. * @return {void}
  12776. */
  12777. renderLabel: function (xy, old, opacity, index) {
  12778. 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;
  12779. if (label && isNumber(x)) {
  12780. label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
  12781. // Apply show first and show last. If the tick is both first and
  12782. // last, it is a single centered tick, in which case we show the
  12783. // label anyway (#2100).
  12784. if ((tick.isFirst &&
  12785. !tick.isLast &&
  12786. !pick(options.showFirstLabel, 1)) ||
  12787. (tick.isLast &&
  12788. !tick.isFirst &&
  12789. !pick(options.showLastLabel, 1))) {
  12790. show = false;
  12791. // Handle label overflow and show or hide accordingly
  12792. }
  12793. else if (horiz &&
  12794. !labelOptions.step &&
  12795. !labelOptions.rotation &&
  12796. !old &&
  12797. opacity !== 0) {
  12798. tick.handleOverflow(xy);
  12799. }
  12800. // apply step
  12801. if (step && index % step) {
  12802. // show those indices dividable by step
  12803. show = false;
  12804. }
  12805. // Set the new position, and show or hide
  12806. if (show && isNumber(xy.y)) {
  12807. xy.opacity = opacity;
  12808. label[tick.isNewLabel ? 'attr' : 'animate'](xy);
  12809. tick.isNewLabel = false;
  12810. }
  12811. else {
  12812. label.attr('y', -9999); // #1338
  12813. tick.isNewLabel = true;
  12814. }
  12815. }
  12816. },
  12817. /**
  12818. * Put everything in place
  12819. *
  12820. * @private
  12821. * @param {number} index
  12822. * @param {boolean} [old]
  12823. * Use old coordinates to prepare an animation into new position
  12824. * @param {number} [opacity]
  12825. * @return {voids}
  12826. */
  12827. render: function (index, old, opacity) {
  12828. 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) ||
  12829. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  12830. opacity = pick(opacity, 1);
  12831. this.isActive = true;
  12832. // Create the grid line
  12833. this.renderGridLine(old, opacity, reverseCrisp);
  12834. // create the tick mark
  12835. this.renderMark(xy, opacity, reverseCrisp);
  12836. // the label is created on init - now move it into place
  12837. this.renderLabel(xy, old, opacity, index);
  12838. tick.isNew = false;
  12839. H.fireEvent(this, 'afterRender');
  12840. },
  12841. /**
  12842. * Destructor for the tick prototype
  12843. *
  12844. * @private
  12845. * @function Highcharts.Tick#destroy
  12846. * @return {void}
  12847. */
  12848. destroy: function () {
  12849. destroyObjectProperties(this, this.axis);
  12850. }
  12851. };
  12852. });
  12853. _registerModule(_modules, 'parts/Axis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  12854. /* *
  12855. *
  12856. * (c) 2010-2019 Torstein Honsi
  12857. *
  12858. * License: www.highcharts.com/license
  12859. *
  12860. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  12861. *
  12862. * */
  12863. /**
  12864. * Options for the path on the Axis to be calculated.
  12865. * @interface Highcharts.AxisPlotLinePathOptionsObject
  12866. */ /**
  12867. * Axis value.
  12868. * @name Highcharts.AxisPlotLinePathOptionsObject#value
  12869. * @type {number|undefined}
  12870. */ /**
  12871. * Line width used for calculation crisp line coordinates. Defaults to 1.
  12872. * @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
  12873. * @type {number|undefined}
  12874. */ /**
  12875. * If `false`, the function will return null when it falls outside the axis
  12876. * bounds. If `true`, the function will return a path aligned to the plot area
  12877. * sides if it falls outside. If `pass`, it will return a path outside.
  12878. * @name Highcharts.AxisPlotLinePathOptionsObject#force
  12879. * @type {string|boolean|undefined}
  12880. */ /**
  12881. * Used in Highstock. When `true`, plot paths (crosshair, plotLines, gridLines)
  12882. * will be rendered on all axes when defined on the first axis.
  12883. * @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
  12884. * @type {boolean|undefined}
  12885. */ /**
  12886. * Use old coordinates (for resizing and rescaling).
  12887. * If not set, defaults to `false`.
  12888. * @name Highcharts.AxisPlotLinePathOptionsObject#old
  12889. * @type {boolean|undefined}
  12890. */ /**
  12891. * If given, return the plot line path of a pixel position on the axis.
  12892. * @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
  12893. * @type {number|undefined}
  12894. */ /**
  12895. * Used in Polar axes. Reverse the positions for concatenation of polygonal
  12896. * plot bands
  12897. * @name Highcharts.AxisPlotLinePathOptionsObject#reverse
  12898. * @type {boolean|undefined}
  12899. */
  12900. /**
  12901. * Options for crosshairs on axes.
  12902. *
  12903. * @product highstock
  12904. *
  12905. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  12906. */
  12907. /**
  12908. * @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
  12909. */
  12910. /**
  12911. * @callback Highcharts.AxisEventCallbackFunction
  12912. *
  12913. * @param {Highcharts.Axis} this
  12914. */
  12915. /**
  12916. * @interface Highcharts.AxisLabelsFormatterContextObject
  12917. */ /**
  12918. * @name Highcharts.AxisLabelsFormatterContextObject#axis
  12919. * @type {Highcharts.Axis}
  12920. */ /**
  12921. * @name Highcharts.AxisLabelsFormatterContextObject#chart
  12922. * @type {Highcharts.Chart}
  12923. */ /**
  12924. * @name Highcharts.AxisLabelsFormatterContextObject#isFirst
  12925. * @type {boolean}
  12926. */ /**
  12927. * @name Highcharts.AxisLabelsFormatterContextObject#isLast
  12928. * @type {boolean}
  12929. */ /**
  12930. * @name Highcharts.AxisLabelsFormatterContextObject#pos
  12931. * @type {number}
  12932. */ /**
  12933. * @name Highcharts.AxisLabelsFormatterContextObject#value
  12934. * @type {number}
  12935. */
  12936. /**
  12937. * Options for axes.
  12938. *
  12939. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  12940. */
  12941. /**
  12942. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  12943. *
  12944. * @param {Highcharts.Axis} this
  12945. *
  12946. * @param {Highcharts.AxisPointBreakEventObject} evt
  12947. */
  12948. /**
  12949. * @interface Highcharts.AxisPointBreakEventObject
  12950. */ /**
  12951. * @name Highcharts.AxisPointBreakEventObject#brk
  12952. * @type {Highcharts.Dictionary<number>}
  12953. */ /**
  12954. * @name Highcharts.AxisPointBreakEventObject#point
  12955. * @type {Highcharts.Point}
  12956. */ /**
  12957. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  12958. * @type {Function}
  12959. */ /**
  12960. * @name Highcharts.AxisPointBreakEventObject#target
  12961. * @type {Highcharts.SVGElement}
  12962. */ /**
  12963. * @name Highcharts.AxisPointBreakEventObject#type
  12964. * @type {"pointBreak"|"pointInBreak"}
  12965. */
  12966. /**
  12967. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  12968. *
  12969. * @param {Highcharts.Axis} this
  12970. *
  12971. * @param {Highcharts.AxisSetExtremesEventObject} evt
  12972. */
  12973. /**
  12974. * @interface Highcharts.AxisSetExtremesEventObject
  12975. * @extends Highcharts.ExtremesObject
  12976. */ /**
  12977. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  12978. * @type {Function}
  12979. */ /**
  12980. * @name Highcharts.AxisSetExtremesEventObject#target
  12981. * @type {Highcharts.SVGElement}
  12982. */ /**
  12983. * @name Highcharts.AxisSetExtremesEventObject#trigger
  12984. * @type {Highcharts.AxisExtremesTriggerValue|string}
  12985. */ /**
  12986. * @name Highcharts.AxisSetExtremesEventObject#type
  12987. * @type {"setExtremes"}
  12988. */
  12989. /**
  12990. * @callback Highcharts.AxisTickPositionerCallbackFunction
  12991. *
  12992. * @param {Highcharts.Axis} this
  12993. *
  12994. * @return {Array<number>}
  12995. */
  12996. /**
  12997. * @interface Highcharts.AxisTickPositionsArray
  12998. * @augments Array<number>
  12999. */
  13000. /**
  13001. * @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
  13002. */
  13003. /**
  13004. * @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
  13005. */
  13006. /**
  13007. * @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
  13008. */
  13009. /**
  13010. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  13011. * function.
  13012. *
  13013. * @interface Highcharts.ExtremesObject
  13014. */ /**
  13015. * The maximum value of the axis' associated series.
  13016. * @name Highcharts.ExtremesObject#dataMax
  13017. * @type {number}
  13018. */ /**
  13019. * The minimum value of the axis' associated series.
  13020. * @name Highcharts.ExtremesObject#dataMin
  13021. * @type {number}
  13022. */ /**
  13023. * The maximum axis value, either automatic or set manually. If the `max` option
  13024. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  13025. * the same as `dataMax`.
  13026. * @name Highcharts.ExtremesObject#max
  13027. * @type {number}
  13028. */ /**
  13029. * The minimum axis value, either automatic or set manually. If the `min` option
  13030. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  13031. * the same as `dataMin`.
  13032. * @name Highcharts.ExtremesObject#min
  13033. * @type {number}
  13034. */ /**
  13035. * The user defined maximum, either from the `max` option or from a zoom or
  13036. * `setExtremes` action.
  13037. * @name Highcharts.ExtremesObject#userMax
  13038. * @type {number}
  13039. */ /**
  13040. * The user defined minimum, either from the `min` option or from a zoom or
  13041. * `setExtremes` action.
  13042. * @name Highcharts.ExtremesObject#userMin
  13043. * @type {number}
  13044. */
  13045. /**
  13046. * Formatter function for the text of a crosshair label.
  13047. *
  13048. * @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
  13049. *
  13050. * @param {Highcharts.Axis} this
  13051. * Axis context
  13052. *
  13053. * @param {number} value
  13054. * Y value of the data point
  13055. *
  13056. * @return {string}
  13057. */
  13058. var animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isString = U.isString, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, splat = U.splat, syncTimeout = U.syncTimeout;
  13059. var addEvent = H.addEvent, color = H.color, defaultOptions = H.defaultOptions, deg2rad = H.deg2rad, fireEvent = H.fireEvent, format = H.format, getMagnitude = H.getMagnitude, merge = H.merge, normalizeTickInterval = H.normalizeTickInterval, removeEvent = H.removeEvent, seriesTypes = H.seriesTypes, Tick = H.Tick;
  13060. /* eslint-disable no-invalid-this, valid-jsdoc */
  13061. /**
  13062. * Create a new axis object. Called internally when instanciating a new chart or
  13063. * adding axes by {@link Highcharts.Chart#addAxis}.
  13064. *
  13065. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  13066. * series cartesian chart, there is one X axis and one Y axis.
  13067. *
  13068. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  13069. * an array of Axis objects. If there is only one axis, it can be referenced
  13070. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  13071. * pattern goes for Y axes.
  13072. *
  13073. * If you need to get the axes from a series object, use the `series.xAxis` and
  13074. * `series.yAxis` properties. These are not arrays, as one series can only be
  13075. * associated to one X and one Y axis.
  13076. *
  13077. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  13078. * the axis configuration options, and get the axis by
  13079. * {@link Highcharts.Chart#get}.
  13080. *
  13081. * Configuration options for the axes are given in options.xAxis and
  13082. * options.yAxis.
  13083. *
  13084. * @class
  13085. * @name Highcharts.Axis
  13086. *
  13087. * @param {Highcharts.Chart} chart
  13088. * The Chart instance to apply the axis on.
  13089. *
  13090. * @param {Highcharts.AxisOptions} options
  13091. * Axis options.
  13092. */
  13093. var Axis = function () {
  13094. this.init.apply(this, arguments);
  13095. /* eslint-enable no-invalid-this, valid-jsdoc */
  13096. };
  13097. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  13098. /**
  13099. * The X axis or category axis. Normally this is the horizontal axis,
  13100. * though if the chart is inverted this is the vertical axis. In case of
  13101. * multiple axes, the xAxis node is an array of configuration objects.
  13102. *
  13103. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  13104. * access to the axis.
  13105. *
  13106. * @productdesc {highmaps}
  13107. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  13108. * control features like zooming and panning. Zooming is in effect the same
  13109. * as setting the extremes of one of the exes.
  13110. *
  13111. * @type {*|Array<*>}
  13112. * @optionparent xAxis
  13113. *
  13114. * @private
  13115. */
  13116. defaultOptions: {
  13117. /**
  13118. * When using multiple axis, the ticks of two or more opposite axes
  13119. * will automatically be aligned by adding ticks to the axis or axes
  13120. * with the least ticks, as if `tickAmount` were specified.
  13121. *
  13122. * This can be prevented by setting `alignTicks` to false. If the grid
  13123. * lines look messy, it's a good idea to hide them for the secondary
  13124. * axis by setting `gridLineWidth` to 0.
  13125. *
  13126. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  13127. * then the `alignTicks ` will be disabled for the Axis.
  13128. *
  13129. * Disabled for logarithmic axes.
  13130. *
  13131. * @type {boolean}
  13132. * @default true
  13133. * @product highcharts highstock gantt
  13134. * @apioption xAxis.alignTicks
  13135. */
  13136. /**
  13137. * Whether to allow decimals in this axis' ticks. When counting
  13138. * integers, like persons or hits on a web page, decimals should
  13139. * be avoided in the labels.
  13140. *
  13141. * @see [minTickInterval](#xAxis.minTickInterval)
  13142. *
  13143. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  13144. * True by default
  13145. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  13146. * False
  13147. *
  13148. * @type {boolean}
  13149. * @default true
  13150. * @since 2.0
  13151. * @apioption xAxis.allowDecimals
  13152. */
  13153. /**
  13154. * When using an alternate grid color, a band is painted across the
  13155. * plot area between every other grid line.
  13156. *
  13157. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  13158. * Alternate grid color on the Y axis
  13159. * @sample {highstock} stock/xaxis/alternategridcolor/
  13160. * Alternate grid color on the Y axis
  13161. *
  13162. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13163. * @apioption xAxis.alternateGridColor
  13164. */
  13165. /**
  13166. * An array defining breaks in the axis, the sections defined will be
  13167. * left out and all the points shifted closer to each other.
  13168. *
  13169. * @productdesc {highcharts}
  13170. * Requires that the broken-axis.js module is loaded.
  13171. *
  13172. * @sample {highcharts} highcharts/axisbreak/break-simple/
  13173. * Simple break
  13174. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  13175. * Advanced with callback
  13176. * @sample {highstock} stock/demo/intraday-breaks/
  13177. * Break on nights and weekends
  13178. *
  13179. * @type {Array<*>}
  13180. * @since 4.1.0
  13181. * @product highcharts highstock gantt
  13182. * @apioption xAxis.breaks
  13183. */
  13184. /**
  13185. * A number indicating how much space should be left between the start
  13186. * and the end of the break. The break size is given in axis units,
  13187. * so for instance on a `datetime` axis, a break size of 3600000 would
  13188. * indicate the equivalent of an hour.
  13189. *
  13190. * @type {number}
  13191. * @default 0
  13192. * @since 4.1.0
  13193. * @product highcharts highstock gantt
  13194. * @apioption xAxis.breaks.breakSize
  13195. */
  13196. /**
  13197. * The point where the break starts.
  13198. *
  13199. * @type {number}
  13200. * @since 4.1.0
  13201. * @product highcharts highstock gantt
  13202. * @apioption xAxis.breaks.from
  13203. */
  13204. /**
  13205. * Defines an interval after which the break appears again. By default
  13206. * the breaks do not repeat.
  13207. *
  13208. * @type {number}
  13209. * @default 0
  13210. * @since 4.1.0
  13211. * @product highcharts highstock gantt
  13212. * @apioption xAxis.breaks.repeat
  13213. */
  13214. /**
  13215. * The point where the break ends.
  13216. *
  13217. * @type {number}
  13218. * @since 4.1.0
  13219. * @product highcharts highstock gantt
  13220. * @apioption xAxis.breaks.to
  13221. */
  13222. /**
  13223. * If categories are present for the xAxis, names are used instead of
  13224. * numbers for that axis.
  13225. *
  13226. * Since Highcharts 3.0, categories can also
  13227. * be extracted by giving each point a [name](#series.data) and setting
  13228. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  13229. * series, best practice remains defining the `categories` array.
  13230. *
  13231. * Example: `categories: ['Apples', 'Bananas', 'Oranges']`
  13232. *
  13233. * @sample {highcharts} highcharts/demo/line-labels/
  13234. * With
  13235. * @sample {highcharts} highcharts/xaxis/categories/
  13236. * Without
  13237. *
  13238. * @type {Array<string>}
  13239. * @product highcharts gantt
  13240. * @apioption xAxis.categories
  13241. */
  13242. /**
  13243. * The highest allowed value for automatically computed axis extremes.
  13244. *
  13245. * @see [floor](#xAxis.floor)
  13246. *
  13247. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  13248. * Floor and ceiling
  13249. *
  13250. * @type {number}
  13251. * @since 4.0
  13252. * @product highcharts highstock gantt
  13253. * @apioption xAxis.ceiling
  13254. */
  13255. /**
  13256. * A class name that opens for styling the axis by CSS, especially in
  13257. * Highcharts styled mode. The class name is applied to group elements
  13258. * for the grid, axis elements and labels.
  13259. *
  13260. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  13261. * Multiple axes with separate styling
  13262. *
  13263. * @type {string}
  13264. * @since 5.0.0
  13265. * @apioption xAxis.className
  13266. */
  13267. /**
  13268. * Configure a crosshair that follows either the mouse pointer or the
  13269. * hovered point.
  13270. *
  13271. * In styled mode, the crosshairs are styled in the
  13272. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  13273. * `.highcharts-xaxis-category` classes.
  13274. *
  13275. * @productdesc {highstock}
  13276. * In Highstock, by default, the crosshair is enabled on the X axis and
  13277. * disabled on the Y axis.
  13278. *
  13279. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  13280. * Crosshair on both axes
  13281. * @sample {highstock} stock/xaxis/crosshairs-xy/
  13282. * Crosshair on both axes
  13283. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  13284. * Crosshair on both axes
  13285. *
  13286. * @declare Highcharts.AxisCrosshairOptions
  13287. * @type {boolean|*}
  13288. * @default false
  13289. * @since 4.1
  13290. * @apioption xAxis.crosshair
  13291. */
  13292. /**
  13293. * A class name for the crosshair, especially as a hook for styling.
  13294. *
  13295. * @type {string}
  13296. * @since 5.0.0
  13297. * @apioption xAxis.crosshair.className
  13298. */
  13299. /**
  13300. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  13301. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  13302. * the crosshair by default highlights the whole category.
  13303. *
  13304. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  13305. * Customized crosshairs
  13306. *
  13307. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13308. * @default #cccccc
  13309. * @since 4.1
  13310. * @apioption xAxis.crosshair.color
  13311. */
  13312. /**
  13313. * The dash style for the crosshair. See
  13314. * [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  13315. * for possible values.
  13316. *
  13317. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  13318. * Dotted crosshair
  13319. * @sample {highstock} stock/xaxis/crosshair-dashed/
  13320. * Dashed X axis crosshair
  13321. *
  13322. * @type {Highcharts.DashStyleValue}
  13323. * @default Solid
  13324. * @since 4.1
  13325. * @apioption xAxis.crosshair.dashStyle
  13326. */
  13327. /**
  13328. * A label on the axis next to the crosshair.
  13329. *
  13330. * In styled mode, the label is styled with the
  13331. * `.highcharts-crosshair-label` class.
  13332. *
  13333. * @sample {highstock} stock/xaxis/crosshair-label/
  13334. * Crosshair labels
  13335. * @sample {highstock} highcharts/css/crosshair-label/
  13336. * Style mode
  13337. *
  13338. * @declare Highcharts.AxisCrosshairLabelOptions
  13339. * @since 2.1
  13340. * @product highstock
  13341. * @apioption xAxis.crosshair.label
  13342. */
  13343. /**
  13344. * Alignment of the label compared to the axis. Defaults to `"left"` for
  13345. * right-side axes, `"right"` for left-side axes and `"center"` for
  13346. * horizontal axes.
  13347. *
  13348. * @type {Highcharts.AlignValue}
  13349. * @since 2.1
  13350. * @product highstock
  13351. * @apioption xAxis.crosshair.label.align
  13352. */
  13353. /**
  13354. * The background color for the label. Defaults to the related series
  13355. * color, or `#666666` if that is not available.
  13356. *
  13357. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13358. * @since 2.1
  13359. * @product highstock
  13360. * @apioption xAxis.crosshair.label.backgroundColor
  13361. */
  13362. /**
  13363. * The border color for the crosshair label
  13364. *
  13365. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13366. * @since 2.1
  13367. * @product highstock
  13368. * @apioption xAxis.crosshair.label.borderColor
  13369. */
  13370. /**
  13371. * The border corner radius of the crosshair label.
  13372. *
  13373. * @type {number}
  13374. * @default 3
  13375. * @since 2.1.10
  13376. * @product highstock
  13377. * @apioption xAxis.crosshair.label.borderRadius
  13378. */
  13379. /**
  13380. * The border width for the crosshair label.
  13381. *
  13382. * @type {number}
  13383. * @default 0
  13384. * @since 2.1
  13385. * @product highstock
  13386. * @apioption xAxis.crosshair.label.borderWidth
  13387. */
  13388. /**
  13389. * Flag to enable crosshair's label.
  13390. *
  13391. * @sample {highstock} stock/xaxis/crosshairs-xy/
  13392. * Enabled label for yAxis' crosshair
  13393. *
  13394. * @type {boolean}
  13395. * @default false
  13396. * @since 2.1
  13397. * @product highstock
  13398. * @apioption xAxis.crosshair.label.enabled
  13399. */
  13400. /**
  13401. * A format string for the crosshair label. Defaults to `{value}` for
  13402. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  13403. *
  13404. * @type {string}
  13405. * @since 2.1
  13406. * @product highstock
  13407. * @apioption xAxis.crosshair.label.format
  13408. */
  13409. /**
  13410. * Formatter function for the label text.
  13411. *
  13412. * @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
  13413. * @since 2.1
  13414. * @product highstock
  13415. * @apioption xAxis.crosshair.label.formatter
  13416. */
  13417. /**
  13418. * Padding inside the crosshair label.
  13419. *
  13420. * @type {number}
  13421. * @default 8
  13422. * @since 2.1
  13423. * @product highstock
  13424. * @apioption xAxis.crosshair.label.padding
  13425. */
  13426. /**
  13427. * The shape to use for the label box.
  13428. *
  13429. * @type {string}
  13430. * @default callout
  13431. * @since 2.1
  13432. * @product highstock
  13433. * @apioption xAxis.crosshair.label.shape
  13434. */
  13435. /**
  13436. * Text styles for the crosshair label.
  13437. *
  13438. * @type {Highcharts.CSSObject}
  13439. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  13440. * @since 2.1
  13441. * @product highstock
  13442. * @apioption xAxis.crosshair.label.style
  13443. */
  13444. /**
  13445. * Whether the crosshair should snap to the point or follow the pointer
  13446. * independent of points.
  13447. *
  13448. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  13449. * True by default
  13450. * @sample {highmaps} maps/demo/latlon-advanced/
  13451. * Snap is false
  13452. *
  13453. * @type {boolean}
  13454. * @default true
  13455. * @since 4.1
  13456. * @apioption xAxis.crosshair.snap
  13457. */
  13458. /**
  13459. * The pixel width of the crosshair. Defaults to 1 for numeric or
  13460. * datetime axes, and for one category width for category axes.
  13461. *
  13462. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  13463. * Customized crosshairs
  13464. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  13465. * Customized crosshairs
  13466. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  13467. * Customized crosshairs
  13468. *
  13469. * @type {number}
  13470. * @default 1
  13471. * @since 4.1
  13472. * @apioption xAxis.crosshair.width
  13473. */
  13474. /**
  13475. * The Z index of the crosshair. Higher Z indices allow drawing the
  13476. * crosshair on top of the series or behind the grid lines.
  13477. *
  13478. * @type {number}
  13479. * @default 2
  13480. * @since 4.1
  13481. * @apioption xAxis.crosshair.zIndex
  13482. */
  13483. /**
  13484. * Whether to zoom axis. If `chart.zoomType` is set, the option allows
  13485. * to disable zooming on an individual axis.
  13486. *
  13487. * @sample {highcharts} highcharts/xaxis/zoomenabled/
  13488. * Zoom enabled is false
  13489. *
  13490. *
  13491. * @type {boolean}
  13492. * @default enabled
  13493. * @apioption xAxis.zoomEnabled
  13494. */
  13495. /**
  13496. * For a datetime axis, the scale will automatically adjust to the
  13497. * appropriate unit. This member gives the default string
  13498. * representations used for each unit. For intermediate values,
  13499. * different units may be used, for example the `day` unit can be used
  13500. * on midnight and `hour` unit be used for intermediate values on the
  13501. * same axis. For an overview of the replacement codes, see
  13502. * [dateFormat](/class-reference/Highcharts#dateFormat).
  13503. *
  13504. * Defaults to:
  13505. * ```js
  13506. * {
  13507. * millisecond: '%H:%M:%S.%L',
  13508. * second: '%H:%M:%S',
  13509. * minute: '%H:%M',
  13510. * hour: '%H:%M',
  13511. * day: '%e. %b',
  13512. * week: '%e. %b',
  13513. * month: '%b \'%y',
  13514. * year: '%Y'
  13515. * }
  13516. * ```
  13517. *
  13518. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  13519. * Different day format on X axis
  13520. * @sample {highstock} stock/xaxis/datetimelabelformats/
  13521. * More information in x axis labels
  13522. *
  13523. * @declare Highcharts.AxisDateTimeLabelFormatsOptions
  13524. * @product highcharts highstock gantt
  13525. */
  13526. dateTimeLabelFormats: {
  13527. /**
  13528. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13529. * @type {string|*}
  13530. */
  13531. millisecond: {
  13532. main: '%H:%M:%S.%L',
  13533. range: false
  13534. },
  13535. /**
  13536. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13537. * @type {string|*}
  13538. */
  13539. second: {
  13540. main: '%H:%M:%S',
  13541. range: false
  13542. },
  13543. /**
  13544. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13545. * @type {string|*}
  13546. */
  13547. minute: {
  13548. main: '%H:%M',
  13549. range: false
  13550. },
  13551. /**
  13552. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13553. * @type {string|*}
  13554. */
  13555. hour: {
  13556. main: '%H:%M',
  13557. range: false
  13558. },
  13559. /**
  13560. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13561. * @type {string|*}
  13562. */
  13563. day: {
  13564. main: '%e. %b'
  13565. },
  13566. /**
  13567. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13568. * @type {string|*}
  13569. */
  13570. week: {
  13571. main: '%e. %b'
  13572. },
  13573. /**
  13574. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13575. * @type {string|*}
  13576. */
  13577. month: {
  13578. main: '%b \'%y'
  13579. },
  13580. /**
  13581. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  13582. * @type {string|*}
  13583. */
  13584. year: {
  13585. main: '%Y'
  13586. }
  13587. },
  13588. /**
  13589. * Whether to force the axis to end on a tick. Use this option with
  13590. * the `maxPadding` option to control the axis end.
  13591. *
  13592. * @productdesc {highstock}
  13593. * In Highstock, `endOnTick` is always false when the navigator or
  13594. * vertical panning is enabled, to prevent jumpy scrolling.
  13595. *
  13596. * @sample {highcharts} highcharts/chart/reflow-true/
  13597. * True by default
  13598. * @sample {highcharts} highcharts/yaxis/endontick/
  13599. * False
  13600. * @sample {highstock} stock/demo/basic-line/
  13601. * True by default
  13602. * @sample {highstock} stock/xaxis/endontick/
  13603. * False
  13604. *
  13605. * @since 1.2.0
  13606. */
  13607. endOnTick: false,
  13608. /**
  13609. * Event handlers for the axis.
  13610. *
  13611. * @type {*}
  13612. * @apioption xAxis.events
  13613. */
  13614. /**
  13615. * An event fired after the breaks have rendered.
  13616. *
  13617. * @see [breaks](#xAxis.breaks)
  13618. *
  13619. * @sample {highcharts} highcharts/axisbreak/break-event/
  13620. * AfterBreak Event
  13621. *
  13622. * @type {Highcharts.AxisEventCallbackFunction}
  13623. * @since 4.1.0
  13624. * @product highcharts gantt
  13625. * @apioption xAxis.events.afterBreaks
  13626. */
  13627. /**
  13628. * As opposed to the `setExtremes` event, this event fires after the
  13629. * final min and max values are computed and corrected for `minRange`.
  13630. *
  13631. * Fires when the minimum and maximum is set for the axis, either by
  13632. * calling the `.setExtremes()` method or by selecting an area in the
  13633. * chart. One parameter, `event`, is passed to the function, containing
  13634. * common event information.
  13635. *
  13636. * The new user set minimum and maximum values can be found by
  13637. * `event.min` and `event.max`. These reflect the axis minimum and
  13638. * maximum in axis values. The actual data extremes are found in
  13639. * `event.dataMin` and `event.dataMax`.
  13640. *
  13641. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  13642. * @since 2.3
  13643. * @context Highcharts.Axis
  13644. * @apioption xAxis.events.afterSetExtremes
  13645. */
  13646. /**
  13647. * An event fired when a break from this axis occurs on a point.
  13648. *
  13649. * @see [breaks](#xAxis.breaks)
  13650. *
  13651. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  13652. * Visualization of a Break
  13653. *
  13654. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  13655. * @since 4.1.0
  13656. * @product highcharts gantt
  13657. * @context Highcharts.Axis
  13658. * @apioption xAxis.events.pointBreak
  13659. */
  13660. /**
  13661. * An event fired when a point falls inside a break from this axis.
  13662. *
  13663. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  13664. * @product highcharts highstock gantt
  13665. * @context Highcharts.Axis
  13666. * @apioption xAxis.events.pointInBreak
  13667. */
  13668. /**
  13669. * Fires when the minimum and maximum is set for the axis, either by
  13670. * calling the `.setExtremes()` method or by selecting an area in the
  13671. * chart. One parameter, `event`, is passed to the function,
  13672. * containing common event information.
  13673. *
  13674. * The new user set minimum and maximum values can be found by
  13675. * `event.min` and `event.max`. These reflect the axis minimum and
  13676. * maximum in data values. When an axis is zoomed all the way out from
  13677. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  13678. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  13679. *
  13680. * @sample {highstock} stock/xaxis/events-setextremes/
  13681. * Log new extremes on x axis
  13682. *
  13683. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  13684. * @since 1.2.0
  13685. * @context Highcharts.Axis
  13686. * @apioption xAxis.events.setExtremes
  13687. */
  13688. /**
  13689. * The lowest allowed value for automatically computed axis extremes.
  13690. *
  13691. * @see [ceiling](#yAxis.ceiling)
  13692. *
  13693. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  13694. * Floor and ceiling
  13695. * @sample {highstock} stock/demo/lazy-loading/
  13696. * Prevent negative stock price on Y axis
  13697. *
  13698. * @type {number}
  13699. * @since 4.0
  13700. * @product highcharts highstock gantt
  13701. * @apioption xAxis.floor
  13702. */
  13703. /**
  13704. * The dash or dot style of the grid lines. For possible values, see
  13705. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  13706. *
  13707. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  13708. * Long dashes
  13709. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  13710. * Long dashes
  13711. *
  13712. * @type {Highcharts.DashStyleValue}
  13713. * @default Solid
  13714. * @since 1.2
  13715. * @apioption xAxis.gridLineDashStyle
  13716. */
  13717. /**
  13718. * The Z index of the grid lines.
  13719. *
  13720. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  13721. * A Z index of 4 renders the grid above the graph
  13722. *
  13723. * @type {number}
  13724. * @default 1
  13725. * @product highcharts highstock gantt
  13726. * @apioption xAxis.gridZIndex
  13727. */
  13728. /**
  13729. * An id for the axis. This can be used after render time to get
  13730. * a pointer to the axis object through `chart.get()`.
  13731. *
  13732. * @sample {highcharts} highcharts/xaxis/id/
  13733. * Get the object
  13734. * @sample {highstock} stock/xaxis/id/
  13735. * Get the object
  13736. *
  13737. * @type {string}
  13738. * @since 1.2.0
  13739. * @apioption xAxis.id
  13740. */
  13741. /**
  13742. * The axis labels show the number or category for each tick.
  13743. *
  13744. * Since v8.0.0: Labels are animated in categorized x-axis with
  13745. * updating data if `tickInterval` and `step` is set to 1.
  13746. *
  13747. * @productdesc {highmaps}
  13748. * X and Y axis labels are by default disabled in Highmaps, but the
  13749. * functionality is inherited from Highcharts and used on `colorAxis`,
  13750. * and can be enabled on X and Y axes too.
  13751. */
  13752. labels: {
  13753. /**
  13754. * What part of the string the given position is anchored to.
  13755. * If `left`, the left side of the string is at the axis position.
  13756. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  13757. * an intelligent guess based on which side of the chart the axis
  13758. * is on and the rotation of the label.
  13759. *
  13760. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  13761. *
  13762. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  13763. * Left
  13764. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  13765. * Right
  13766. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  13767. * Left-aligned labels on a vertical category axis
  13768. *
  13769. * @type {Highcharts.AlignValue}
  13770. * @apioption xAxis.labels.align
  13771. */
  13772. /**
  13773. * For horizontal axes, the allowed degrees of label rotation
  13774. * to prevent overlapping labels. If there is enough space,
  13775. * labels are not rotated. As the chart gets narrower, it
  13776. * will start rotating the labels -45 degrees, then remove
  13777. * every second label and try again with rotations 0 and -45 etc.
  13778. * Set it to `false` to disable rotation, which will
  13779. * cause the labels to word-wrap if possible.
  13780. *
  13781. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  13782. * Default auto rotation of 0 or -45
  13783. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  13784. * Custom graded auto rotation
  13785. *
  13786. * @type {Array<number>|false}
  13787. * @default [-45]
  13788. * @since 4.1.0
  13789. * @product highcharts highstock gantt
  13790. * @apioption xAxis.labels.autoRotation
  13791. */
  13792. /**
  13793. * When each category width is more than this many pixels, we don't
  13794. * apply auto rotation. Instead, we lay out the axis label with word
  13795. * wrap. A lower limit makes sense when the label contains multiple
  13796. * short words that don't extend the available horizontal space for
  13797. * each label.
  13798. *
  13799. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  13800. * Lower limit
  13801. *
  13802. * @type {number}
  13803. * @default 80
  13804. * @since 4.1.5
  13805. * @product highcharts gantt
  13806. * @apioption xAxis.labels.autoRotationLimit
  13807. */
  13808. /**
  13809. * Polar charts only. The label's pixel distance from the perimeter
  13810. * of the plot area.
  13811. *
  13812. * @type {number}
  13813. * @default 15
  13814. * @product highcharts gantt
  13815. * @apioption xAxis.labels.distance
  13816. */
  13817. /**
  13818. * Enable or disable the axis labels.
  13819. *
  13820. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  13821. * X axis labels disabled
  13822. * @sample {highstock} stock/xaxis/labels-enabled/
  13823. * X axis labels disabled
  13824. *
  13825. * @default {highcharts|highstock|gantt} true
  13826. * @default {highmaps} false
  13827. */
  13828. enabled: true,
  13829. /**
  13830. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  13831. * for the axis label.
  13832. *
  13833. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  13834. * Add units to Y axis label
  13835. *
  13836. * @type {string}
  13837. * @default {value}
  13838. * @since 3.0
  13839. * @apioption xAxis.labels.format
  13840. */
  13841. /**
  13842. * Callback JavaScript function to format the label. The value
  13843. * is given by `this.value`. Additional properties for `this` are
  13844. * `axis`, `chart`, `isFirst` and `isLast`. The value of the default
  13845. * label formatter can be retrieved by calling
  13846. * `this.axis.defaultLabelFormatter.call(this)` within the function.
  13847. *
  13848. * Defaults to:
  13849. * ```js
  13850. * function() {
  13851. * return this.value;
  13852. * }
  13853. * ```
  13854. *
  13855. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  13856. * Linked category names
  13857. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  13858. * Modified numeric labels
  13859. * @sample {highstock} stock/xaxis/labels-formatter/
  13860. * Added units on Y axis
  13861. *
  13862. * @type {Highcharts.FormatterCallbackFunction<Highcharts.AxisLabelsFormatterContextObject>}
  13863. * @apioption xAxis.labels.formatter
  13864. */
  13865. /**
  13866. * The number of pixels to indent the labels per level in a treegrid
  13867. * axis.
  13868. *
  13869. * @sample gantt/treegrid-axis/demo
  13870. * Indentation 10px by default.
  13871. * @sample gantt/treegrid-axis/indentation-0px
  13872. * Indentation set to 0px.
  13873. *
  13874. * @product gantt
  13875. */
  13876. indentation: 10,
  13877. /**
  13878. * Horizontal axis only. When `staggerLines` is not set,
  13879. * `maxStaggerLines` defines how many lines the axis is allowed to
  13880. * add to automatically avoid overlapping X labels. Set to `1` to
  13881. * disable overlap detection.
  13882. *
  13883. * @deprecated
  13884. * @type {number}
  13885. * @default 5
  13886. * @since 1.3.3
  13887. * @apioption xAxis.labels.maxStaggerLines
  13888. */
  13889. /**
  13890. * How to handle overflowing labels on horizontal axis. If set to
  13891. * `"allow"`, it will not be aligned at all. By default it
  13892. * `"justify"` labels inside the chart area. If there is room to
  13893. * move it, it will be aligned to the edge, else it will be removed.
  13894. *
  13895. * @type {string}
  13896. * @default justify
  13897. * @since 2.2.5
  13898. * @validvalue ["allow", "justify"]
  13899. * @apioption xAxis.labels.overflow
  13900. */
  13901. /**
  13902. * The pixel padding for axis labels, to ensure white space between
  13903. * them.
  13904. *
  13905. * @type {number}
  13906. * @default 5
  13907. * @product highcharts gantt
  13908. * @apioption xAxis.labels.padding
  13909. */
  13910. /**
  13911. * Whether to reserve space for the labels. By default, space is
  13912. * reserved for the labels in these cases:
  13913. *
  13914. * * On all horizontal axes.
  13915. * * On vertical axes if `label.align` is `right` on a left-side
  13916. * axis or `left` on a right-side axis.
  13917. * * On vertical axes if `label.align` is `center`.
  13918. *
  13919. * This can be turned off when for example the labels are rendered
  13920. * inside the plot area instead of outside.
  13921. *
  13922. * @see [labels.align](#xAxis.labels.align)
  13923. *
  13924. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  13925. * No reserved space, labels inside plot
  13926. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  13927. * Left-aligned labels on a vertical category axis
  13928. *
  13929. * @type {boolean}
  13930. * @since 4.1.10
  13931. * @product highcharts gantt
  13932. * @apioption xAxis.labels.reserveSpace
  13933. */
  13934. /**
  13935. * Rotation of the labels in degrees.
  13936. *
  13937. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  13938. * X axis labels rotated 90°
  13939. *
  13940. * @type {number}
  13941. * @default 0
  13942. * @apioption xAxis.labels.rotation
  13943. */
  13944. /**
  13945. * Horizontal axes only. The number of lines to spread the labels
  13946. * over to make room or tighter labels.
  13947. *
  13948. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  13949. * Show labels over two lines
  13950. * @sample {highstock} stock/xaxis/labels-staggerlines/
  13951. * Show labels over two lines
  13952. *
  13953. * @type {number}
  13954. * @since 2.1
  13955. * @apioption xAxis.labels.staggerLines
  13956. */
  13957. /**
  13958. * To show only every _n_'th label on the axis, set the step to _n_.
  13959. * Setting the step to 2 shows every other label.
  13960. *
  13961. * By default, the step is calculated automatically to avoid
  13962. * overlap. To prevent this, set it to 1\. This usually only
  13963. * happens on a category axis, and is often a sign that you have
  13964. * chosen the wrong axis type.
  13965. *
  13966. * Read more at
  13967. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  13968. * => What axis should I use?
  13969. *
  13970. * @sample {highcharts} highcharts/xaxis/labels-step/
  13971. * Showing only every other axis label on a categorized
  13972. * x-axis
  13973. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  13974. * Auto steps on a category axis
  13975. *
  13976. * @type {number}
  13977. * @since 2.1
  13978. * @apioption xAxis.labels.step
  13979. */
  13980. /**
  13981. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  13982. * to render the labels.
  13983. *
  13984. * @type {boolean}
  13985. * @default false
  13986. * @apioption xAxis.labels.useHTML
  13987. */
  13988. /**
  13989. * The x position offset of the label relative to the tick position
  13990. * on the axis.
  13991. *
  13992. * @sample {highcharts} highcharts/xaxis/labels-x/
  13993. * Y axis labels placed on grid lines
  13994. */
  13995. x: 0,
  13996. /**
  13997. * The y position offset of the label relative to the tick position
  13998. * on the axis. The default makes it adapt to the font size on
  13999. * bottom axis.
  14000. *
  14001. * @sample {highcharts} highcharts/xaxis/labels-x/
  14002. * Y axis labels placed on grid lines
  14003. *
  14004. * @type {number}
  14005. * @apioption xAxis.labels.y
  14006. */
  14007. /**
  14008. * The Z index for the axis labels.
  14009. *
  14010. * @type {number}
  14011. * @default 7
  14012. * @apioption xAxis.labels.zIndex
  14013. */
  14014. /**
  14015. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  14016. * wrapping of category labels. Use `textOverflow: 'none'` to
  14017. * prevent ellipsis (dots).
  14018. *
  14019. * In styled mode, the labels are styled with the
  14020. * `.highcharts-axis-labels` class.
  14021. *
  14022. * @sample {highcharts} highcharts/xaxis/labels-style/
  14023. * Red X axis labels
  14024. *
  14025. * @type {Highcharts.CSSObject}
  14026. */
  14027. style: {
  14028. /** @internal */
  14029. color: '#666666',
  14030. /** @internal */
  14031. cursor: 'default',
  14032. /** @internal */
  14033. fontSize: '11px'
  14034. }
  14035. },
  14036. /**
  14037. * The left position as the horizontal axis. If it's a number, it is
  14038. * interpreted as pixel position relative to the chart.
  14039. *
  14040. * Since Highcharts v5.0.13: If it's a percentage string, it is
  14041. * interpreted as percentages of the plot width, offset from plot area
  14042. * left.
  14043. *
  14044. * @type {number|string}
  14045. * @product highcharts highstock
  14046. * @apioption xAxis.left
  14047. */
  14048. /**
  14049. * The top position as the vertical axis. If it's a number, it is
  14050. * interpreted as pixel position relative to the chart.
  14051. *
  14052. * Since Highcharts 2: If it's a percentage string, it is interpreted
  14053. * as percentages of the plot height, offset from plot area top.
  14054. *
  14055. * @type {number|string}
  14056. * @product highcharts highstock
  14057. * @apioption xAxis.top
  14058. */
  14059. /**
  14060. * Index of another axis that this axis is linked to. When an axis is
  14061. * linked to a master axis, it will take the same extremes as
  14062. * the master, but as assigned by min or max or by setExtremes.
  14063. * It can be used to show additional info, or to ease reading the
  14064. * chart by duplicating the scales.
  14065. *
  14066. * @sample {highcharts} highcharts/xaxis/linkedto/
  14067. * Different string formats of the same date
  14068. * @sample {highcharts} highcharts/yaxis/linkedto/
  14069. * Y values on both sides
  14070. *
  14071. * @type {number}
  14072. * @since 2.0.2
  14073. * @product highcharts highstock gantt
  14074. * @apioption xAxis.linkedTo
  14075. */
  14076. /**
  14077. * The maximum value of the axis. If `null`, the max value is
  14078. * automatically calculated.
  14079. *
  14080. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  14081. * might be rounded up.
  14082. *
  14083. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  14084. * beyond the set max in order to reach the given number of ticks. The
  14085. * same may happen in a chart with multiple axes, determined by [chart.
  14086. * alignTicks](#chart), where a `tickAmount` is applied internally.
  14087. *
  14088. * @sample {highcharts} highcharts/yaxis/max-200/
  14089. * Y axis max of 200
  14090. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  14091. * Y axis max on logarithmic axis
  14092. * @sample {highstock} stock/xaxis/min-max/
  14093. * Fixed min and max on X axis
  14094. * @sample {highmaps} maps/axis/min-max/
  14095. * Pre-zoomed to a specific area
  14096. *
  14097. * @type {number|null}
  14098. * @apioption xAxis.max
  14099. */
  14100. /**
  14101. * Padding of the max value relative to the length of the axis. A
  14102. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  14103. * when you don't want the highest data value to appear on the edge
  14104. * of the plot area. When the axis' `max` option is set or a max extreme
  14105. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  14106. *
  14107. * @sample {highcharts} highcharts/yaxis/maxpadding/
  14108. * Max padding of 0.25 on y axis
  14109. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  14110. * Greater min- and maxPadding
  14111. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  14112. * Add some padding
  14113. *
  14114. * @default {highcharts} 0.01
  14115. * @default {highstock|highmaps} 0
  14116. * @since 1.2.0
  14117. */
  14118. maxPadding: 0.01,
  14119. /**
  14120. * Deprecated. Use `minRange` instead.
  14121. *
  14122. * @deprecated
  14123. * @type {number}
  14124. * @product highcharts highstock
  14125. * @apioption xAxis.maxZoom
  14126. */
  14127. /**
  14128. * The minimum value of the axis. If `null` the min value is
  14129. * automatically calculated.
  14130. *
  14131. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  14132. * the `min` value might be rounded down.
  14133. *
  14134. * The automatically calculated minimum value is also affected by
  14135. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  14136. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  14137. * as well as [series.threshold](#plotOptions.series.threshold)
  14138. * and [series.softThreshold](#plotOptions.series.softThreshold).
  14139. *
  14140. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  14141. * -50 with startOnTick to false
  14142. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  14143. * -50 with startOnTick true by default
  14144. * @sample {highstock} stock/xaxis/min-max/
  14145. * Set min and max on X axis
  14146. * @sample {highmaps} maps/axis/min-max/
  14147. * Pre-zoomed to a specific area
  14148. *
  14149. * @type {number|null}
  14150. * @apioption xAxis.min
  14151. */
  14152. /**
  14153. * The dash or dot style of the minor grid lines. For possible values,
  14154. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  14155. *
  14156. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  14157. * Long dashes on minor grid lines
  14158. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  14159. * Long dashes on minor grid lines
  14160. *
  14161. * @type {Highcharts.DashStyleValue}
  14162. * @default Solid
  14163. * @since 1.2
  14164. * @apioption xAxis.minorGridLineDashStyle
  14165. */
  14166. /**
  14167. * Specific tick interval in axis units for the minor ticks. On a linear
  14168. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  14169. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  14170. * shown.
  14171. *
  14172. * On logarithmic axes, the unit is the power of the value. For example,
  14173. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  14174. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  14175. * between 1 and 10, 10 and 100 etc.
  14176. *
  14177. * If user settings dictate minor ticks to become too dense, they don't
  14178. * make sense, and will be ignored to prevent performance problems.
  14179. *
  14180. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  14181. * Null by default
  14182. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  14183. * 5 units
  14184. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  14185. * "auto"
  14186. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  14187. * 0.1
  14188. * @sample {highstock} stock/demo/basic-line/
  14189. * Null by default
  14190. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  14191. * "auto"
  14192. *
  14193. * @type {number|string|null}
  14194. * @apioption xAxis.minorTickInterval
  14195. */
  14196. /**
  14197. * The pixel length of the minor tick marks.
  14198. *
  14199. * @sample {highcharts} highcharts/yaxis/minorticklength/
  14200. * 10px on Y axis
  14201. * @sample {highstock} stock/xaxis/minorticks/
  14202. * 10px on Y axis
  14203. */
  14204. minorTickLength: 2,
  14205. /**
  14206. * The position of the minor tick marks relative to the axis line.
  14207. * Can be one of `inside` and `outside`.
  14208. *
  14209. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  14210. * Outside by default
  14211. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  14212. * Inside
  14213. * @sample {highstock} stock/xaxis/minorticks/
  14214. * Inside
  14215. *
  14216. * @validvalue ["inside", "outside"]
  14217. */
  14218. minorTickPosition: 'outside',
  14219. /**
  14220. * Enable or disable minor ticks. Unless
  14221. * [minorTickInterval](#xAxis.minorTickInterval) is set, the tick
  14222. * interval is calculated as a fifth of the `tickInterval`.
  14223. *
  14224. * On a logarithmic axis, minor ticks are laid out based on a best
  14225. * guess, attempting to enter approximately 5 minor ticks between
  14226. * each major tick.
  14227. *
  14228. * Prior to v6.0.0, ticks were unabled in auto layout by setting
  14229. * `minorTickInterval` to `"auto"`.
  14230. *
  14231. * @productdesc {highcharts}
  14232. * On axes using [categories](#xAxis.categories), minor ticks are not
  14233. * supported.
  14234. *
  14235. * @sample {highcharts} highcharts/yaxis/minorticks-true/
  14236. * Enabled on linear Y axis
  14237. *
  14238. * @type {boolean}
  14239. * @default false
  14240. * @since 6.0.0
  14241. * @apioption xAxis.minorTicks
  14242. */
  14243. /**
  14244. * The pixel width of the minor tick mark.
  14245. *
  14246. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  14247. * 3px width
  14248. * @sample {highstock} stock/xaxis/minorticks/
  14249. * 1px width
  14250. *
  14251. * @type {number}
  14252. * @default 0
  14253. * @apioption xAxis.minorTickWidth
  14254. */
  14255. /**
  14256. * Padding of the min value relative to the length of the axis. A
  14257. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  14258. * when you don't want the lowest data value to appear on the edge
  14259. * of the plot area. When the axis' `min` option is set or a min extreme
  14260. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  14261. *
  14262. * @sample {highcharts} highcharts/yaxis/minpadding/
  14263. * Min padding of 0.2
  14264. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  14265. * Greater min- and maxPadding
  14266. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  14267. * Add some padding
  14268. *
  14269. * @default {highcharts} 0.01
  14270. * @default {highstock|highmaps} 0
  14271. * @since 1.2.0
  14272. * @product highcharts highstock gantt
  14273. */
  14274. minPadding: 0.01,
  14275. /**
  14276. * The minimum range to display on this axis. The entire axis will not
  14277. * be allowed to span over a smaller interval than this. For example,
  14278. * for a datetime axis the main unit is milliseconds. If minRange is
  14279. * set to 3600000, you can't zoom in more than to one hour.
  14280. *
  14281. * The default minRange for the x axis is five times the smallest
  14282. * interval between any of the data points.
  14283. *
  14284. * On a logarithmic axis, the unit for the minimum range is the power.
  14285. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  14286. * 100-1000, 1000-10000 etc.
  14287. *
  14288. * Note that the `minPadding`, `maxPadding`, `startOnTick` and
  14289. * `endOnTick` settings also affect how the extremes of the axis
  14290. * are computed.
  14291. *
  14292. * @sample {highcharts} highcharts/xaxis/minrange/
  14293. * Minimum range of 5
  14294. * @sample {highstock} stock/xaxis/minrange/
  14295. * Max zoom of 6 months overrides user selections
  14296. * @sample {highmaps} maps/axis/minrange/
  14297. * Minimum range of 1000
  14298. *
  14299. * @type {number}
  14300. * @apioption xAxis.minRange
  14301. */
  14302. /**
  14303. * The minimum tick interval allowed in axis values. For example on
  14304. * zooming in on an axis with daily data, this can be used to prevent
  14305. * the axis from showing hours. Defaults to the closest distance between
  14306. * two points on the axis.
  14307. *
  14308. * @type {number}
  14309. * @since 2.3.0
  14310. * @apioption xAxis.minTickInterval
  14311. */
  14312. /**
  14313. * The distance in pixels from the plot area to the axis line.
  14314. * A positive offset moves the axis with it's line, labels and ticks
  14315. * away from the plot area. This is typically used when two or more
  14316. * axes are displayed on the same side of the plot. With multiple
  14317. * axes the offset is dynamically adjusted to avoid collision, this
  14318. * can be overridden by setting offset explicitly.
  14319. *
  14320. * @sample {highcharts} highcharts/yaxis/offset/
  14321. * Y axis offset of 70
  14322. * @sample {highcharts} highcharts/yaxis/offset-centered/
  14323. * Axes positioned in the center of the plot
  14324. * @sample {highstock} stock/xaxis/offset/
  14325. * Y axis offset by 70 px
  14326. *
  14327. * @type {number}
  14328. * @default 0
  14329. * @apioption xAxis.offset
  14330. */
  14331. /**
  14332. * Whether to display the axis on the opposite side of the normal. The
  14333. * normal is on the left side for vertical axes and bottom for
  14334. * horizontal, so the opposite sides will be right and top respectively.
  14335. * This is typically used with dual or multiple axes.
  14336. *
  14337. * @sample {highcharts} highcharts/yaxis/opposite/
  14338. * Secondary Y axis opposite
  14339. * @sample {highstock} stock/xaxis/opposite/
  14340. * Y axis on left side
  14341. *
  14342. * @type {boolean}
  14343. * @default false
  14344. * @apioption xAxis.opposite
  14345. */
  14346. /**
  14347. * In an ordinal axis, the points are equally spaced in the chart
  14348. * regardless of the actual time or x distance between them. This means
  14349. * that missing data periods (e.g. nights or weekends for a stock chart)
  14350. * will not take up space in the chart.
  14351. * Having `ordinal: false` will show any gaps created by the `gapSize`
  14352. * setting proportionate to their duration.
  14353. *
  14354. * In stock charts the X axis is ordinal by default, unless
  14355. * the boost module is used and at least one of the series' data length
  14356. * exceeds the [boostThreshold](#series.line.boostThreshold).
  14357. *
  14358. * @sample {highstock} stock/xaxis/ordinal-true/
  14359. * True by default
  14360. * @sample {highstock} stock/xaxis/ordinal-false/
  14361. * False
  14362. *
  14363. * @type {boolean}
  14364. * @default true
  14365. * @since 1.1
  14366. * @product highstock
  14367. * @apioption xAxis.ordinal
  14368. */
  14369. /**
  14370. * Additional range on the right side of the xAxis. Works similar to
  14371. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  14372. * both main `xAxis` and the navigator's `xAxis`.
  14373. *
  14374. * @sample {highstock} stock/xaxis/overscroll/
  14375. * One minute overscroll with live data
  14376. *
  14377. * @type {number}
  14378. * @default 0
  14379. * @since 6.0.0
  14380. * @product highstock
  14381. * @apioption xAxis.overscroll
  14382. */
  14383. /**
  14384. * Refers to the index in the [panes](#panes) array. Used for circular
  14385. * gauges and polar charts. When the option is not set then first pane
  14386. * will be used.
  14387. *
  14388. * @sample highcharts/demo/gauge-vu-meter
  14389. * Two gauges with different center
  14390. *
  14391. * @type {number}
  14392. * @product highcharts
  14393. * @apioption xAxis.pane
  14394. */
  14395. /**
  14396. * The zoomed range to display when only defining one or none of `min`
  14397. * or `max`. For example, to show the latest month, a range of one month
  14398. * can be set.
  14399. *
  14400. * @sample {highstock} stock/xaxis/range/
  14401. * Setting a zoomed range when the rangeSelector is disabled
  14402. *
  14403. * @type {number}
  14404. * @product highstock
  14405. * @apioption xAxis.range
  14406. */
  14407. /**
  14408. * Whether to reverse the axis so that the highest number is closest
  14409. * to the origin. If the chart is inverted, the x axis is reversed by
  14410. * default.
  14411. *
  14412. * @sample {highcharts} highcharts/yaxis/reversed/
  14413. * Reversed Y axis
  14414. * @sample {highstock} stock/xaxis/reversed/
  14415. * Reversed Y axis
  14416. *
  14417. * @type {boolean}
  14418. * @default false
  14419. * @apioption xAxis.reversed
  14420. */
  14421. // reversed: false,
  14422. /**
  14423. * This option determines how stacks should be ordered within a group.
  14424. * For example reversed xAxis also reverses stacks, so first series
  14425. * comes last in a group. To keep order like for non-reversed xAxis
  14426. * enable this option.
  14427. *
  14428. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  14429. * Reversed stacks comparison
  14430. * @sample {highstock} highcharts/xaxis/reversedstacks/
  14431. * Reversed stacks comparison
  14432. *
  14433. * @type {boolean}
  14434. * @default false
  14435. * @since 6.1.1
  14436. * @product highcharts highstock
  14437. * @apioption xAxis.reversedStacks
  14438. */
  14439. /**
  14440. * An optional scrollbar to display on the X axis in response to
  14441. * limiting the minimum and maximum of the axis values.
  14442. *
  14443. * In styled mode, all the presentational options for the scrollbar are
  14444. * replaced by the classes `.highcharts-scrollbar-thumb`,
  14445. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  14446. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  14447. *
  14448. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  14449. * Heatmap with both scrollbars
  14450. *
  14451. * @extends scrollbar
  14452. * @since 4.2.6
  14453. * @product highstock
  14454. * @apioption xAxis.scrollbar
  14455. */
  14456. /**
  14457. * Whether to show the axis line and title when the axis has no data.
  14458. *
  14459. * @sample {highcharts} highcharts/yaxis/showempty/
  14460. * When clicking the legend to hide series, one axis preserves
  14461. * line and title, the other doesn't
  14462. * @sample {highstock} highcharts/yaxis/showempty/
  14463. * When clicking the legend to hide series, one axis preserves
  14464. * line and title, the other doesn't
  14465. *
  14466. * @since 1.1
  14467. */
  14468. showEmpty: true,
  14469. /**
  14470. * Whether to show the first tick label.
  14471. *
  14472. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  14473. * Set to false on X axis
  14474. * @sample {highstock} stock/xaxis/showfirstlabel/
  14475. * Labels below plot lines on Y axis
  14476. *
  14477. * @type {boolean}
  14478. * @default true
  14479. * @apioption xAxis.showFirstLabel
  14480. */
  14481. /**
  14482. * Whether to show the last tick label. Defaults to `true` on cartesian
  14483. * charts, and `false` on polar charts.
  14484. *
  14485. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  14486. * Set to true on X axis
  14487. * @sample {highstock} stock/xaxis/showfirstlabel/
  14488. * Labels below plot lines on Y axis
  14489. *
  14490. * @type {boolean}
  14491. * @default true
  14492. * @product highcharts highstock gantt
  14493. * @apioption xAxis.showLastLabel
  14494. */
  14495. /**
  14496. * A soft maximum for the axis. If the series data maximum is less than
  14497. * this, the axis will stay at this maximum, but if the series data
  14498. * maximum is higher, the axis will flex to show all data.
  14499. *
  14500. * @sample highcharts/yaxis/softmin-softmax/
  14501. * Soft min and max
  14502. *
  14503. * @type {number}
  14504. * @since 5.0.1
  14505. * @product highcharts highstock gantt
  14506. * @apioption xAxis.softMax
  14507. */
  14508. /**
  14509. * A soft minimum for the axis. If the series data minimum is greater
  14510. * than this, the axis will stay at this minimum, but if the series
  14511. * data minimum is lower, the axis will flex to show all data.
  14512. *
  14513. * @sample highcharts/yaxis/softmin-softmax/
  14514. * Soft min and max
  14515. *
  14516. * @type {number}
  14517. * @since 5.0.1
  14518. * @product highcharts highstock gantt
  14519. * @apioption xAxis.softMin
  14520. */
  14521. /**
  14522. * For datetime axes, this decides where to put the tick between weeks.
  14523. * 0 = Sunday, 1 = Monday.
  14524. *
  14525. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  14526. * Monday by default
  14527. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  14528. * Sunday
  14529. * @sample {highstock} stock/xaxis/startofweek-1
  14530. * Monday by default
  14531. * @sample {highstock} stock/xaxis/startofweek-0
  14532. * Sunday
  14533. *
  14534. * @product highcharts highstock gantt
  14535. */
  14536. startOfWeek: 1,
  14537. /**
  14538. * Whether to force the axis to start on a tick. Use this option with
  14539. * the `minPadding` option to control the axis start.
  14540. *
  14541. * @productdesc {highstock}
  14542. * In Highstock, `startOnTick` is always false when either the
  14543. * navigator or vertical panning is enabled, to prevent jumpy
  14544. * scrolling.
  14545. *
  14546. * @sample {highcharts} highcharts/xaxis/startontick-false/
  14547. * False by default
  14548. * @sample {highcharts} highcharts/xaxis/startontick-true/
  14549. * True
  14550. * @sample {highstock} stock/xaxis/endontick/
  14551. * False for Y axis
  14552. *
  14553. * @since 1.2.0
  14554. */
  14555. startOnTick: false,
  14556. /**
  14557. * The amount of ticks to draw on the axis. This opens up for aligning
  14558. * the ticks of multiple charts or panes within a chart. This option
  14559. * overrides the `tickPixelInterval` option.
  14560. *
  14561. * This option only has an effect on linear axes. Datetime, logarithmic
  14562. * or category axes are not affected.
  14563. *
  14564. * @sample {highcharts} highcharts/yaxis/tickamount/
  14565. * 8 ticks on Y axis
  14566. * @sample {highstock} highcharts/yaxis/tickamount/
  14567. * 8 ticks on Y axis
  14568. *
  14569. * @type {number}
  14570. * @since 4.1.0
  14571. * @product highcharts highstock gantt
  14572. * @apioption xAxis.tickAmount
  14573. */
  14574. /**
  14575. * The interval of the tick marks in axis units. When `undefined`, the
  14576. * tick interval is computed to approximately follow the
  14577. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  14578. * axes. On categorized axes, a `undefined` tickInterval will default to
  14579. * 1, one category. Note that datetime axes are based on milliseconds,
  14580. * so for example an interval of one day is expressed as
  14581. * `24 * 3600 * 1000`.
  14582. *
  14583. * On logarithmic axes, the tickInterval is based on powers, so a
  14584. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  14585. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  14586. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  14587. * 40 etc.
  14588. *
  14589. *
  14590. * If the tickInterval is too dense for labels to be drawn, Highcharts
  14591. * may remove ticks.
  14592. *
  14593. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  14594. * option may interfere with the `tickInterval` setting.
  14595. *
  14596. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  14597. * @see [tickPositions](#xAxis.tickPositions)
  14598. * @see [tickPositioner](#xAxis.tickPositioner)
  14599. *
  14600. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  14601. * Tick interval of 5 on a linear axis
  14602. * @sample {highstock} stock/xaxis/tickinterval/
  14603. * Tick interval of 0.01 on Y axis
  14604. *
  14605. * @type {number}
  14606. * @apioption xAxis.tickInterval
  14607. */
  14608. /**
  14609. * The pixel length of the main tick marks.
  14610. *
  14611. * @sample {highcharts} highcharts/xaxis/ticklength/
  14612. * 20 px tick length on the X axis
  14613. * @sample {highstock} stock/xaxis/ticks/
  14614. * Formatted ticks on X axis
  14615. */
  14616. tickLength: 10,
  14617. /**
  14618. * If tickInterval is `null` this option sets the approximate pixel
  14619. * interval of the tick marks. Not applicable to categorized axis.
  14620. *
  14621. * The tick interval is also influenced by the [minTickInterval](
  14622. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  14623. * being denser than the data points.
  14624. *
  14625. * @see [tickInterval](#xAxis.tickInterval)
  14626. * @see [tickPositioner](#xAxis.tickPositioner)
  14627. * @see [tickPositions](#xAxis.tickPositions)
  14628. *
  14629. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  14630. * 50 px on X axis
  14631. * @sample {highstock} stock/xaxis/tickpixelinterval/
  14632. * 200 px on X axis
  14633. */
  14634. tickPixelInterval: 100,
  14635. /**
  14636. * For categorized axes only. If `on` the tick mark is placed in the
  14637. * center of the category, if `between` the tick mark is placed between
  14638. * categories. The default is `between` if the `tickInterval` is 1, else
  14639. * `on`.
  14640. *
  14641. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  14642. * "between" by default
  14643. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  14644. * "on"
  14645. *
  14646. * @product highcharts gantt
  14647. * @validvalue ["on", "between"]
  14648. */
  14649. tickmarkPlacement: 'between',
  14650. /**
  14651. * The position of the major tick marks relative to the axis line.
  14652. * Can be one of `inside` and `outside`.
  14653. *
  14654. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  14655. * "outside" by default
  14656. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  14657. * "inside"
  14658. * @sample {highstock} stock/xaxis/ticks/
  14659. * Formatted ticks on X axis
  14660. *
  14661. * @validvalue ["inside", "outside"]
  14662. */
  14663. tickPosition: 'outside',
  14664. /**
  14665. * A callback function returning array defining where the ticks are
  14666. * laid out on the axis. This overrides the default behaviour of
  14667. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  14668. * #xAxis.tickInterval). The automatic tick positions are accessible
  14669. * through `this.tickPositions` and can be modified by the callback.
  14670. *
  14671. * @see [tickPositions](#xAxis.tickPositions)
  14672. *
  14673. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  14674. * Demo of tickPositions and tickPositioner
  14675. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  14676. * Demo of tickPositions and tickPositioner
  14677. *
  14678. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  14679. * @apioption xAxis.tickPositioner
  14680. */
  14681. /**
  14682. * An array defining where the ticks are laid out on the axis. This
  14683. * overrides the default behaviour of [tickPixelInterval](
  14684. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  14685. *
  14686. * @see [tickPositioner](#xAxis.tickPositioner)
  14687. *
  14688. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  14689. * Demo of tickPositions and tickPositioner
  14690. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  14691. * Demo of tickPositions and tickPositioner
  14692. *
  14693. * @type {Array<number>}
  14694. * @apioption xAxis.tickPositions
  14695. */
  14696. /**
  14697. * The pixel width of the major tick marks. Defaults to 0 on category
  14698. * axes, otherwise 1.
  14699. *
  14700. * In styled mode, the stroke width is given in the `.highcharts-tick`
  14701. * class, but in order for the element to be generated on category axes,
  14702. * the option must be explicitly set to 1.
  14703. *
  14704. * @sample {highcharts} highcharts/xaxis/tickwidth/
  14705. * 10 px width
  14706. * @sample {highcharts} highcharts/css/axis-grid/
  14707. * Styled mode
  14708. * @sample {highstock} stock/xaxis/ticks/
  14709. * Formatted ticks on X axis
  14710. * @sample {highstock} highcharts/css/axis-grid/
  14711. * Styled mode
  14712. *
  14713. * @type {undefined|number}
  14714. * @default {highstock} 1
  14715. * @default {highmaps} 0
  14716. * @apioption xAxis.tickWidth
  14717. */
  14718. /**
  14719. * The axis title, showing next to the axis line.
  14720. *
  14721. * @productdesc {highmaps}
  14722. * In Highmaps, the axis is hidden by default, but adding an axis title
  14723. * is still possible. X axis and Y axis titles will appear at the bottom
  14724. * and left by default.
  14725. */
  14726. title: {
  14727. /**
  14728. * Deprecated. Set the `text` to `null` to disable the title.
  14729. *
  14730. * @deprecated
  14731. * @type {boolean}
  14732. * @product highcharts
  14733. * @apioption xAxis.title.enabled
  14734. */
  14735. /**
  14736. * The pixel distance between the axis labels or line and the title.
  14737. * Defaults to 0 for horizontal axes, 10 for vertical
  14738. *
  14739. * @sample {highcharts} highcharts/xaxis/title-margin/
  14740. * Y axis title margin of 60
  14741. *
  14742. * @type {number}
  14743. * @apioption xAxis.title.margin
  14744. */
  14745. /**
  14746. * The distance of the axis title from the axis line. By default,
  14747. * this distance is computed from the offset width of the labels,
  14748. * the labels' distance from the axis and the title's margin.
  14749. * However when the offset option is set, it overrides all this.
  14750. *
  14751. * @sample {highcharts} highcharts/yaxis/title-offset/
  14752. * Place the axis title on top of the axis
  14753. * @sample {highstock} highcharts/yaxis/title-offset/
  14754. * Place the axis title on top of the Y axis
  14755. *
  14756. * @type {number}
  14757. * @since 2.2.0
  14758. * @apioption xAxis.title.offset
  14759. */
  14760. /**
  14761. * Whether to reserve space for the title when laying out the axis.
  14762. *
  14763. * @type {boolean}
  14764. * @default true
  14765. * @since 5.0.11
  14766. * @product highcharts highstock gantt
  14767. * @apioption xAxis.title.reserveSpace
  14768. */
  14769. /**
  14770. * The rotation of the text in degrees. 0 is horizontal, 270 is
  14771. * vertical reading from bottom to top.
  14772. *
  14773. * @sample {highcharts} highcharts/yaxis/title-offset/
  14774. * Horizontal
  14775. *
  14776. * @type {number}
  14777. * @default 0
  14778. * @apioption xAxis.title.rotation
  14779. */
  14780. /**
  14781. * The actual text of the axis title. It can contain basic HTML tags
  14782. * like `b`, `i` and `span` with style.
  14783. *
  14784. * @sample {highcharts} highcharts/xaxis/title-text/
  14785. * Custom HTML
  14786. * @sample {highstock} stock/xaxis/title-text/
  14787. * Titles for both axes
  14788. *
  14789. * @type {string|null}
  14790. * @apioption xAxis.title.text
  14791. */
  14792. /**
  14793. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  14794. * Default alignment depends on the
  14795. * [title.align](xAxis.title.align):
  14796. *
  14797. * Horizontal axes:
  14798. * - for `align` = `"low"`, `textAlign` is set to `left`
  14799. * - for `align` = `"middle"`, `textAlign` is set to `center`
  14800. * - for `align` = `"high"`, `textAlign` is set to `right`
  14801. *
  14802. * Vertical axes:
  14803. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  14804. * set to `right`
  14805. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  14806. * set to `left`
  14807. * - for `align` = `"middle"`, `textAlign` is set to `center`
  14808. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  14809. * set to `left`
  14810. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  14811. * set to `right`
  14812. *
  14813. * @type {Highcharts.AlignValue}
  14814. * @apioption xAxis.title.textAlign
  14815. */
  14816. /**
  14817. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  14818. * to render the axis title.
  14819. *
  14820. * @type {boolean}
  14821. * @default false
  14822. * @product highcharts highstock gantt
  14823. * @apioption xAxis.title.useHTML
  14824. */
  14825. /**
  14826. * Horizontal pixel offset of the title position.
  14827. *
  14828. * @type {number}
  14829. * @default 0
  14830. * @since 4.1.6
  14831. * @product highcharts highstock gantt
  14832. * @apioption xAxis.title.x
  14833. */
  14834. /**
  14835. * Vertical pixel offset of the title position.
  14836. *
  14837. * @type {number}
  14838. * @product highcharts highstock gantt
  14839. * @apioption xAxis.title.y
  14840. */
  14841. /**
  14842. * Alignment of the title relative to the axis values. Possible
  14843. * values are "low", "middle" or "high".
  14844. *
  14845. * @sample {highcharts} highcharts/xaxis/title-align-low/
  14846. * "low"
  14847. * @sample {highcharts} highcharts/xaxis/title-align-center/
  14848. * "middle" by default
  14849. * @sample {highcharts} highcharts/xaxis/title-align-high/
  14850. * "high"
  14851. * @sample {highcharts} highcharts/yaxis/title-offset/
  14852. * Place the Y axis title on top of the axis
  14853. * @sample {highstock} stock/xaxis/title-align/
  14854. * Aligned to "high" value
  14855. *
  14856. * @type {Highcharts.AxisTitleAlignValue}
  14857. */
  14858. align: 'middle',
  14859. /**
  14860. * CSS styles for the title. If the title text is longer than the
  14861. * axis length, it will wrap to multiple lines by default. This can
  14862. * be customized by setting `textOverflow: 'ellipsis'`, by
  14863. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  14864. *
  14865. * In styled mode, the stroke width is given in the
  14866. * `.highcharts-axis-title` class.
  14867. *
  14868. * @sample {highcharts} highcharts/xaxis/title-style/
  14869. * Red
  14870. * @sample {highcharts} highcharts/css/axis/
  14871. * Styled mode
  14872. *
  14873. * @type {Highcharts.CSSObject}
  14874. */
  14875. style: {
  14876. /** @internal */
  14877. color: '#666666'
  14878. }
  14879. },
  14880. /**
  14881. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  14882. * or `category`. In a datetime axis, the numbers are given in
  14883. * milliseconds, and tick marks are placed on appropriate values like
  14884. * full hours or days. In a category axis, the
  14885. * [point names](#series.line.data.name) of the chart's series are used
  14886. * for categories, if not a [categories](#xAxis.categories) array is
  14887. * defined.
  14888. *
  14889. * @sample {highcharts} highcharts/xaxis/type-linear/
  14890. * Linear
  14891. * @sample {highcharts} highcharts/yaxis/type-log/
  14892. * Logarithmic
  14893. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  14894. * Logarithmic with minor grid lines
  14895. * @sample {highcharts} highcharts/xaxis/type-log-both/
  14896. * Logarithmic on two axes
  14897. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  14898. * Logarithmic with extension to emulate negative values
  14899. *
  14900. * @type {Highcharts.AxisTypeValue}
  14901. * @product highcharts gantt
  14902. */
  14903. type: 'linear',
  14904. /**
  14905. * If there are multiple axes on the same side of the chart, the pixel
  14906. * margin between the axes. Defaults to 0 on vertical axes, 15 on
  14907. * horizontal axes.
  14908. *
  14909. * @type {number}
  14910. * @since 7.0.3
  14911. * @apioption xAxis.margin
  14912. */
  14913. /**
  14914. * Applies only when the axis `type` is `category`. When `uniqueNames`
  14915. * is true, points are placed on the X axis according to their names.
  14916. * If the same point name is repeated in the same or another series,
  14917. * the point is placed on the same X position as other points of the
  14918. * same name. When `uniqueNames` is false, the points are laid out in
  14919. * increasing X positions regardless of their names, and the X axis
  14920. * category will take the name of the last point in each position.
  14921. *
  14922. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  14923. * True by default
  14924. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  14925. * False
  14926. *
  14927. * @type {boolean}
  14928. * @default true
  14929. * @since 4.2.7
  14930. * @product highcharts gantt
  14931. * @apioption xAxis.uniqueNames
  14932. */
  14933. /**
  14934. * Datetime axis only. An array determining what time intervals the
  14935. * ticks are allowed to fall on. Each array item is an array where the
  14936. * first value is the time unit and the second value another array of
  14937. * allowed multiples.
  14938. *
  14939. * Defaults to:
  14940. * ```js
  14941. * units: [[
  14942. * 'millisecond', // unit name
  14943. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  14944. * ], [
  14945. * 'second',
  14946. * [1, 2, 5, 10, 15, 30]
  14947. * ], [
  14948. * 'minute',
  14949. * [1, 2, 5, 10, 15, 30]
  14950. * ], [
  14951. * 'hour',
  14952. * [1, 2, 3, 4, 6, 8, 12]
  14953. * ], [
  14954. * 'day',
  14955. * [1]
  14956. * ], [
  14957. * 'week',
  14958. * [1]
  14959. * ], [
  14960. * 'month',
  14961. * [1, 3, 6]
  14962. * ], [
  14963. * 'year',
  14964. * null
  14965. * ]]
  14966. * ```
  14967. *
  14968. * @type {Array<Array<string,(Array<number>|null)>>}
  14969. * @product highcharts highstock gantt
  14970. * @apioption xAxis.units
  14971. */
  14972. /**
  14973. * Whether axis, including axis title, line, ticks and labels, should
  14974. * be visible.
  14975. *
  14976. * @type {boolean}
  14977. * @default true
  14978. * @since 4.1.9
  14979. * @product highcharts highstock gantt
  14980. * @apioption xAxis.visible
  14981. */
  14982. /**
  14983. * Color of the minor, secondary grid lines.
  14984. *
  14985. * In styled mode, the stroke width is given in the
  14986. * `.highcharts-minor-grid-line` class.
  14987. *
  14988. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  14989. * Bright grey lines from Y axis
  14990. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  14991. * Styled mode
  14992. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  14993. * Bright grey lines from Y axis
  14994. *
  14995. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  14996. * @default #f2f2f2
  14997. */
  14998. minorGridLineColor: '#f2f2f2',
  14999. /**
  15000. * Width of the minor, secondary grid lines.
  15001. *
  15002. * In styled mode, the stroke width is given in the
  15003. * `.highcharts-grid-line` class.
  15004. *
  15005. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  15006. * 2px lines from Y axis
  15007. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15008. * Styled mode
  15009. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  15010. * 2px lines from Y axis
  15011. */
  15012. minorGridLineWidth: 1,
  15013. /**
  15014. * Color for the minor tick marks.
  15015. *
  15016. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  15017. * Black tick marks on Y axis
  15018. * @sample {highstock} stock/xaxis/minorticks/
  15019. * Black tick marks on Y axis
  15020. *
  15021. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15022. * @default #999999
  15023. */
  15024. minorTickColor: '#999999',
  15025. /**
  15026. * The color of the line marking the axis itself.
  15027. *
  15028. * In styled mode, the line stroke is given in the
  15029. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  15030. *
  15031. * @productdesc {highmaps}
  15032. * In Highmaps, the axis line is hidden by default, because the axis is
  15033. * not visible by default.
  15034. *
  15035. * @sample {highcharts} highcharts/yaxis/linecolor/
  15036. * A red line on Y axis
  15037. * @sample {highcharts|highstock} highcharts/css/axis/
  15038. * Axes in styled mode
  15039. * @sample {highstock} stock/xaxis/linecolor/
  15040. * A red line on X axis
  15041. *
  15042. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15043. * @default #ccd6eb
  15044. */
  15045. lineColor: '#ccd6eb',
  15046. /**
  15047. * The width of the line marking the axis itself.
  15048. *
  15049. * In styled mode, the stroke width is given in the
  15050. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  15051. *
  15052. * @sample {highcharts} highcharts/yaxis/linecolor/
  15053. * A 1px line on Y axis
  15054. * @sample {highcharts|highstock} highcharts/css/axis/
  15055. * Axes in styled mode
  15056. * @sample {highstock} stock/xaxis/linewidth/
  15057. * A 2px line on X axis
  15058. *
  15059. * @default {highcharts|highstock} 1
  15060. * @default {highmaps} 0
  15061. */
  15062. lineWidth: 1,
  15063. /**
  15064. * Color of the grid lines extending the ticks across the plot area.
  15065. *
  15066. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  15067. * class.
  15068. *
  15069. * @productdesc {highmaps}
  15070. * In Highmaps, the grid lines are hidden by default.
  15071. *
  15072. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  15073. * Green lines
  15074. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15075. * Styled mode
  15076. * @sample {highstock} stock/xaxis/gridlinecolor/
  15077. * Green lines
  15078. *
  15079. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15080. * @default #e6e6e6
  15081. */
  15082. gridLineColor: '#e6e6e6',
  15083. // gridLineDashStyle: 'solid',
  15084. /**
  15085. * The width of the grid lines extending the ticks across the plot area.
  15086. *
  15087. * In styled mode, the stroke width is given in the
  15088. * `.highcharts-grid-line` class.
  15089. *
  15090. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  15091. * 2px lines
  15092. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15093. * Styled mode
  15094. * @sample {highstock} stock/xaxis/gridlinewidth/
  15095. * 2px lines
  15096. *
  15097. * @type {number}
  15098. * @default 0
  15099. * @apioption xAxis.gridLineWidth
  15100. */
  15101. // gridLineWidth: 0,
  15102. /**
  15103. * The height as the vertical axis. If it's a number, it is
  15104. * interpreted as pixels.
  15105. *
  15106. * Since Highcharts 2: If it's a percentage string, it is interpreted
  15107. * as percentages of the total plot height.
  15108. *
  15109. * @type {number|string}
  15110. * @product highcharts highstock
  15111. * @apioption xAxis.height
  15112. */
  15113. /**
  15114. * The width as the horizontal axis. If it's a number, it is interpreted
  15115. * as pixels.
  15116. *
  15117. * Since Highcharts v5.0.13: If it's a percentage string, it is
  15118. * interpreted as percentages of the total plot width.
  15119. *
  15120. * @type {number|string}
  15121. * @product highcharts highstock
  15122. * @apioption xAxis.width
  15123. */
  15124. /**
  15125. * Color for the main tick marks.
  15126. *
  15127. * In styled mode, the stroke is given in the `.highcharts-tick`
  15128. * class.
  15129. *
  15130. * @sample {highcharts} highcharts/xaxis/tickcolor/
  15131. * Red ticks on X axis
  15132. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15133. * Styled mode
  15134. * @sample {highstock} stock/xaxis/ticks/
  15135. * Formatted ticks on X axis
  15136. *
  15137. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15138. * @default #ccd6eb
  15139. */
  15140. tickColor: '#ccd6eb'
  15141. // tickWidth: 1
  15142. },
  15143. /**
  15144. * The Y axis or value axis. Normally this is the vertical axis,
  15145. * though if the chart is inverted this is the horizontal axis.
  15146. * In case of multiple axes, the yAxis node is an array of
  15147. * configuration objects.
  15148. *
  15149. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  15150. * access to the axis.
  15151. *
  15152. * @type {*|Array<*>}
  15153. * @extends xAxis
  15154. * @excluding currentDateIndicator,ordinal,overscroll
  15155. * @optionparent yAxis
  15156. *
  15157. * @private
  15158. */
  15159. defaultYAxisOptions: {
  15160. /**
  15161. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  15162. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  15163. * `linear` for other chart types.
  15164. *
  15165. * In a datetime axis, the numbers are given in milliseconds, and tick
  15166. * marks are placed on appropriate values, like full hours or days. In a
  15167. * category or treegrid axis, the [point names](#series.line.data.name)
  15168. * of the chart's series are used for categories, if a
  15169. * [categories](#xAxis.categories) array is not defined.
  15170. *
  15171. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  15172. * Logarithmic with minor grid lines
  15173. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  15174. * Logarithmic with extension to emulate negative values
  15175. * @sample {gantt} gantt/treegrid-axis/demo
  15176. * Treegrid axis
  15177. *
  15178. * @type {Highcharts.AxisTypeValue}
  15179. * @default {highcharts} linear
  15180. * @default {gantt} treegrid
  15181. * @product highcharts gantt
  15182. * @apioption yAxis.type
  15183. */
  15184. /**
  15185. * The height of the Y axis. If it's a number, it is interpreted as
  15186. * pixels.
  15187. *
  15188. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  15189. * percentages of the total plot height.
  15190. *
  15191. * @see [yAxis.top](#yAxis.top)
  15192. *
  15193. * @sample {highstock} stock/demo/candlestick-and-volume/
  15194. * Percentage height panes
  15195. *
  15196. * @type {number|string}
  15197. * @product highcharts highstock
  15198. * @apioption yAxis.height
  15199. */
  15200. /**
  15201. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  15202. * to represent the maximum value of the Y axis.
  15203. *
  15204. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  15205. * Min and max colors
  15206. *
  15207. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15208. * @default #003399
  15209. * @since 4.0
  15210. * @product highcharts
  15211. * @apioption yAxis.maxColor
  15212. */
  15213. /**
  15214. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  15215. * to represent the minimum value of the Y axis.
  15216. *
  15217. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  15218. * Min and max color
  15219. *
  15220. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15221. * @default #e6ebf5
  15222. * @since 4.0
  15223. * @product highcharts
  15224. * @apioption yAxis.minColor
  15225. */
  15226. /**
  15227. * Whether to reverse the axis so that the highest number is closest
  15228. * to the origin.
  15229. *
  15230. * @sample {highcharts} highcharts/yaxis/reversed/
  15231. * Reversed Y axis
  15232. * @sample {highstock} stock/xaxis/reversed/
  15233. * Reversed Y axis
  15234. *
  15235. * @type {boolean}
  15236. * @default {highcharts} false
  15237. * @default {highstock} false
  15238. * @default {highmaps} true
  15239. * @default {gantt} true
  15240. * @apioption yAxis.reversed
  15241. */
  15242. /**
  15243. * If `true`, the first series in a stack will be drawn on top in a
  15244. * positive, non-reversed Y axis. If `false`, the first series is in
  15245. * the base of the stack.
  15246. *
  15247. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  15248. * Non-reversed stacks
  15249. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  15250. * Non-reversed stacks
  15251. *
  15252. * @type {boolean}
  15253. * @default true
  15254. * @since 3.0.10
  15255. * @product highcharts highstock
  15256. * @apioption yAxis.reversedStacks
  15257. */
  15258. /**
  15259. * Solid gauge series only. Color stops for the solid gauge. Use this
  15260. * in cases where a linear gradient between a `minColor` and `maxColor`
  15261. * is not sufficient. The stops is an array of tuples, where the first
  15262. * item is a float between 0 and 1 assigning the relative position in
  15263. * the gradient, and the second item is the color.
  15264. *
  15265. * For solid gauges, the Y axis also inherits the concept of
  15266. * [data classes](http://api.highcharts.com/highmaps#colorAxis.dataClasses)
  15267. * from the Highmaps color axis.
  15268. *
  15269. * @see [minColor](#yAxis.minColor)
  15270. * @see [maxColor](#yAxis.maxColor)
  15271. *
  15272. * @sample {highcharts} highcharts/demo/gauge-solid/
  15273. * True by default
  15274. *
  15275. * @type {Array<Highcharts.GradientColorStopObject>}
  15276. * @since 4.0
  15277. * @product highcharts
  15278. * @apioption yAxis.stops
  15279. */
  15280. /**
  15281. * The pixel width of the major tick marks.
  15282. *
  15283. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  15284. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  15285. *
  15286. * @type {number}
  15287. * @default 0
  15288. * @product highcharts highstock gantt
  15289. * @apioption yAxis.tickWidth
  15290. */
  15291. /**
  15292. * Angular gauges and solid gauges only.
  15293. * The label's pixel distance from the perimeter of the plot area.
  15294. *
  15295. * Since v7.1.2: If it's a percentage string, it is interpreted the
  15296. * same as [series.radius](#plotOptions.gauge.radius), so label can be
  15297. * aligned under the gauge's shape.
  15298. *
  15299. * @sample {highcharts} highcharts/yaxis/labels-distance/
  15300. * Labels centered under the arc
  15301. *
  15302. * @type {number|string}
  15303. * @default -25
  15304. * @product highcharts
  15305. * @apioption yAxis.labels.distance
  15306. */
  15307. /**
  15308. * The y position offset of the label relative to the tick position
  15309. * on the axis.
  15310. *
  15311. * @sample {highcharts} highcharts/xaxis/labels-x/
  15312. * Y axis labels placed on grid lines
  15313. *
  15314. * @type {number}
  15315. * @default {highcharts} 3
  15316. * @default {highstock} -2
  15317. * @default {highmaps} 3
  15318. * @apioption yAxis.labels.y
  15319. */
  15320. /**
  15321. * @productdesc {highstock}
  15322. * In Highstock, `endOnTick` is always false when either the
  15323. * navigator or vertical panning is enabled, to prevent jumpy
  15324. * scrolling.
  15325. */
  15326. endOnTick: true,
  15327. /**
  15328. * Padding of the max value relative to the length of the axis. A
  15329. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15330. * when you don't want the highest data value to appear on the edge
  15331. * of the plot area. When the axis' `max` option is set or a max extreme
  15332. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15333. *
  15334. * Also the `softThreshold` option takes precedence over `maxPadding`,
  15335. * so if the data is tangent to the threshold, `maxPadding` may not
  15336. * apply unless `softThreshold` is set to false.
  15337. *
  15338. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  15339. * Max padding of 0.2
  15340. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15341. * Greater min- and maxPadding
  15342. *
  15343. * @since 1.2.0
  15344. * @product highcharts highstock gantt
  15345. */
  15346. maxPadding: 0.05,
  15347. /**
  15348. * Padding of the min value relative to the length of the axis. A
  15349. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15350. * when you don't want the lowest data value to appear on the edge
  15351. * of the plot area. When the axis' `min` option is set or a max extreme
  15352. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15353. *
  15354. * Also the `softThreshold` option takes precedence over `minPadding`,
  15355. * so if the data is tangent to the threshold, `minPadding` may not
  15356. * apply unless `softThreshold` is set to false.
  15357. *
  15358. * @sample {highcharts} highcharts/yaxis/minpadding/
  15359. * Min padding of 0.2
  15360. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15361. * Greater min- and maxPadding
  15362. *
  15363. * @since 1.2.0
  15364. * @product highcharts highstock gantt
  15365. */
  15366. minPadding: 0.05,
  15367. /**
  15368. * @productdesc {highstock}
  15369. * In Highstock 1.x, the Y axis was placed on the left side by default.
  15370. *
  15371. * @sample {highcharts} highcharts/yaxis/opposite/
  15372. * Secondary Y axis opposite
  15373. * @sample {highstock} stock/xaxis/opposite/
  15374. * Y axis on left side
  15375. *
  15376. * @type {boolean}
  15377. * @default {highstock} true
  15378. * @default {highcharts} false
  15379. * @product highstock highcharts gantt
  15380. * @apioption yAxis.opposite
  15381. */
  15382. /**
  15383. * @see [tickInterval](#xAxis.tickInterval)
  15384. * @see [tickPositioner](#xAxis.tickPositioner)
  15385. * @see [tickPositions](#xAxis.tickPositions)
  15386. */
  15387. tickPixelInterval: 72,
  15388. showLastLabel: true,
  15389. /**
  15390. * @extends xAxis.labels
  15391. */
  15392. labels: {
  15393. /**
  15394. * What part of the string the given position is anchored to. Can
  15395. * be one of `"left"`, `"center"` or `"right"`. The exact position
  15396. * also depends on the `labels.x` setting.
  15397. *
  15398. * Angular gauges and solid gauges defaults to `"center"`.
  15399. * Solid gauges with two labels have additional option `"auto"`
  15400. * for automatic horizontal and vertical alignment.
  15401. *
  15402. * @see [yAxis.labels.distance](#yAxis.labels.distance)
  15403. *
  15404. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  15405. * Left
  15406. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  15407. * Solid gauge labels auto aligned
  15408. *
  15409. * @type {Highcharts.AlignValue}
  15410. * @default {highcharts|highmaps} right
  15411. * @default {highstock} left
  15412. * @apioption yAxis.labels.align
  15413. */
  15414. /**
  15415. * The x position offset of the label relative to the tick position
  15416. * on the axis. Defaults to -15 for left axis, 15 for right axis.
  15417. *
  15418. * @sample {highcharts} highcharts/xaxis/labels-x/
  15419. * Y axis labels placed on grid lines
  15420. */
  15421. x: -8
  15422. },
  15423. /**
  15424. * @productdesc {highmaps}
  15425. * In Highmaps, the axis line is hidden by default, because the axis is
  15426. * not visible by default.
  15427. *
  15428. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  15429. * @apioption yAxis.lineColor
  15430. */
  15431. /**
  15432. * @sample {highcharts} highcharts/yaxis/max-200/
  15433. * Y axis max of 200
  15434. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  15435. * Y axis max on logarithmic axis
  15436. * @sample {highstock} stock/yaxis/min-max/
  15437. * Fixed min and max on Y axis
  15438. * @sample {highmaps} maps/axis/min-max/
  15439. * Pre-zoomed to a specific area
  15440. *
  15441. * @apioption yAxis.max
  15442. */
  15443. /**
  15444. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  15445. * -50 with startOnTick to false
  15446. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  15447. * -50 with startOnTick true by default
  15448. * @sample {highstock} stock/yaxis/min-max/
  15449. * Fixed min and max on Y axis
  15450. * @sample {highmaps} maps/axis/min-max/
  15451. * Pre-zoomed to a specific area
  15452. *
  15453. * @apioption yAxis.min
  15454. */
  15455. /**
  15456. * An optional scrollbar to display on the Y axis in response to
  15457. * limiting the minimum an maximum of the axis values.
  15458. *
  15459. * In styled mode, all the presentational options for the scrollbar
  15460. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  15461. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  15462. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  15463. *
  15464. * @sample {highstock} stock/yaxis/scrollbar/
  15465. * Scrollbar on the Y axis
  15466. *
  15467. * @extends scrollbar
  15468. * @since 4.2.6
  15469. * @product highstock
  15470. * @excluding height
  15471. * @apioption yAxis.scrollbar
  15472. */
  15473. /**
  15474. * Enable the scrollbar on the Y axis.
  15475. *
  15476. * @sample {highstock} stock/yaxis/scrollbar/
  15477. * Enabled on Y axis
  15478. *
  15479. * @type {boolean}
  15480. * @default false
  15481. * @since 4.2.6
  15482. * @product highstock
  15483. * @apioption yAxis.scrollbar.enabled
  15484. */
  15485. /**
  15486. * Pixel margin between the scrollbar and the axis elements.
  15487. *
  15488. * @type {number}
  15489. * @default 10
  15490. * @since 4.2.6
  15491. * @product highstock
  15492. * @apioption yAxis.scrollbar.margin
  15493. */
  15494. /**
  15495. * Whether to show the scrollbar when it is fully zoomed out at max
  15496. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  15497. * hidden until the user zooms in, like common in browsers.
  15498. *
  15499. * @type {boolean}
  15500. * @default true
  15501. * @since 4.2.6
  15502. * @product highstock
  15503. * @apioption yAxis.scrollbar.showFull
  15504. */
  15505. /**
  15506. * The width of a vertical scrollbar or height of a horizontal
  15507. * scrollbar. Defaults to 20 on touch devices.
  15508. *
  15509. * @type {number}
  15510. * @default 14
  15511. * @since 4.2.6
  15512. * @product highstock
  15513. * @apioption yAxis.scrollbar.size
  15514. */
  15515. /**
  15516. * Z index of the scrollbar elements.
  15517. *
  15518. * @type {number}
  15519. * @default 3
  15520. * @since 4.2.6
  15521. * @product highstock
  15522. * @apioption yAxis.scrollbar.zIndex
  15523. */
  15524. /**
  15525. * A soft maximum for the axis. If the series data maximum is less
  15526. * than this, the axis will stay at this maximum, but if the series
  15527. * data maximum is higher, the axis will flex to show all data.
  15528. *
  15529. * **Note**: The [series.softThreshold](
  15530. * #plotOptions.series.softThreshold) option takes precedence over this
  15531. * option.
  15532. *
  15533. * @sample highcharts/yaxis/softmin-softmax/
  15534. * Soft min and max
  15535. *
  15536. * @type {number}
  15537. * @since 5.0.1
  15538. * @product highcharts highstock gantt
  15539. * @apioption yAxis.softMax
  15540. */
  15541. /**
  15542. * A soft minimum for the axis. If the series data minimum is greater
  15543. * than this, the axis will stay at this minimum, but if the series
  15544. * data minimum is lower, the axis will flex to show all data.
  15545. *
  15546. * **Note**: The [series.softThreshold](
  15547. * #plotOptions.series.softThreshold) option takes precedence over this
  15548. * option.
  15549. *
  15550. * @sample highcharts/yaxis/softmin-softmax/
  15551. * Soft min and max
  15552. *
  15553. * @type {number}
  15554. * @since 5.0.1
  15555. * @product highcharts highstock gantt
  15556. * @apioption yAxis.softMin
  15557. */
  15558. /**
  15559. * Defines the horizontal alignment of the stack total label. Can be one
  15560. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  15561. * at runtime and depends on orientation and whether the stack is
  15562. * positive or negative.
  15563. *
  15564. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  15565. * Aligned to the left
  15566. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  15567. * Aligned in center
  15568. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  15569. * Aligned to the right
  15570. *
  15571. * @type {Highcharts.AlignValue}
  15572. * @since 2.1.5
  15573. * @product highcharts
  15574. * @apioption yAxis.stackLabels.align
  15575. */
  15576. /**
  15577. * A [format string](http://docs.highcharts.com/#formatting) for the
  15578. * data label. Available variables are the same as for `formatter`.
  15579. *
  15580. * @type {string}
  15581. * @default {total}
  15582. * @since 3.0.2
  15583. * @product highcharts highstock
  15584. * @apioption yAxis.stackLabels.format
  15585. */
  15586. /**
  15587. * Rotation of the labels in degrees.
  15588. *
  15589. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  15590. * Labels rotated 45°
  15591. *
  15592. * @type {number}
  15593. * @default 0
  15594. * @since 2.1.5
  15595. * @product highcharts
  15596. * @apioption yAxis.stackLabels.rotation
  15597. */
  15598. /**
  15599. * The text alignment for the label. While `align` determines where the
  15600. * texts anchor point is placed with regards to the stack, `textAlign`
  15601. * determines how the text is aligned against its anchor point. Possible
  15602. * values are `"left"`, `"center"` and `"right"`. The default value is
  15603. * calculated at runtime and depends on orientation and whether the
  15604. * stack is positive or negative.
  15605. *
  15606. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  15607. * Label in center position but text-aligned left
  15608. *
  15609. * @type {Highcharts.AlignValue}
  15610. * @since 2.1.5
  15611. * @product highcharts
  15612. * @apioption yAxis.stackLabels.textAlign
  15613. */
  15614. /**
  15615. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  15616. * to render the labels.
  15617. *
  15618. * @type {boolean}
  15619. * @default false
  15620. * @since 3.0
  15621. * @product highcharts highstock
  15622. * @apioption yAxis.stackLabels.useHTML
  15623. */
  15624. /**
  15625. * Defines the vertical alignment of the stack total label. Can be one
  15626. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  15627. * at runtime and depends on orientation and whether the stack is
  15628. * positive or negative.
  15629. *
  15630. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  15631. * Vertically aligned top
  15632. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  15633. * Vertically aligned middle
  15634. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  15635. * Vertically aligned bottom
  15636. *
  15637. * @type {Highcharts.VerticalAlignValue}
  15638. * @since 2.1.5
  15639. * @product highcharts
  15640. * @apioption yAxis.stackLabels.verticalAlign
  15641. */
  15642. /**
  15643. * The x position offset of the label relative to the left of the
  15644. * stacked bar. The default value is calculated at runtime and depends
  15645. * on orientation and whether the stack is positive or negative.
  15646. *
  15647. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  15648. * Stack total labels with x offset
  15649. *
  15650. * @type {number}
  15651. * @since 2.1.5
  15652. * @product highcharts
  15653. * @apioption yAxis.stackLabels.x
  15654. */
  15655. /**
  15656. * The y position offset of the label relative to the tick position
  15657. * on the axis. The default value is calculated at runtime and depends
  15658. * on orientation and whether the stack is positive or negative.
  15659. *
  15660. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  15661. * Stack total labels with y offset
  15662. *
  15663. * @type {number}
  15664. * @since 2.1.5
  15665. * @product highcharts
  15666. * @apioption yAxis.stackLabels.y
  15667. */
  15668. /**
  15669. * Whether to force the axis to start on a tick. Use this option with
  15670. * the `maxPadding` option to control the axis start.
  15671. *
  15672. * @sample {highcharts} highcharts/xaxis/startontick-false/
  15673. * False by default
  15674. * @sample {highcharts} highcharts/xaxis/startontick-true/
  15675. * True
  15676. * @sample {highstock} stock/xaxis/endontick/
  15677. * False for Y axis
  15678. *
  15679. * @since 1.2.0
  15680. * @product highcharts highstock gantt
  15681. */
  15682. startOnTick: true,
  15683. title: {
  15684. /**
  15685. * The pixel distance between the axis labels and the title.
  15686. * Positive values are outside the axis line, negative are inside.
  15687. *
  15688. * @sample {highcharts} highcharts/xaxis/title-margin/
  15689. * Y axis title margin of 60
  15690. *
  15691. * @type {number}
  15692. * @default 40
  15693. * @apioption yAxis.title.margin
  15694. */
  15695. /**
  15696. * The rotation of the text in degrees. 0 is horizontal, 270 is
  15697. * vertical reading from bottom to top.
  15698. *
  15699. * @sample {highcharts} highcharts/yaxis/title-offset/
  15700. * Horizontal
  15701. */
  15702. rotation: 270,
  15703. /**
  15704. * The actual text of the axis title. Horizontal texts can contain
  15705. * HTML, but rotated texts are painted using vector techniques and
  15706. * must be clean text. The Y axis title is disabled by setting the
  15707. * `text` option to `undefined`.
  15708. *
  15709. * @sample {highcharts} highcharts/xaxis/title-text/
  15710. * Custom HTML
  15711. *
  15712. * @type {string|null}
  15713. * @default {highcharts} Values
  15714. * @default {highstock} undefined
  15715. * @product highcharts highstock gantt
  15716. */
  15717. text: 'Values'
  15718. },
  15719. /**
  15720. * The top position of the Y axis. If it's a number, it is interpreted
  15721. * as pixel position relative to the chart.
  15722. *
  15723. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  15724. * percentages of the plot height, offset from plot area top.
  15725. *
  15726. * @see [yAxis.height](#yAxis.height)
  15727. *
  15728. * @sample {highstock} stock/demo/candlestick-and-volume/
  15729. * Percentage height panes
  15730. *
  15731. * @type {number|string}
  15732. * @product highcharts highstock
  15733. * @apioption yAxis.top
  15734. */
  15735. /**
  15736. * The stack labels show the total value for each bar in a stacked
  15737. * column or bar chart. The label will be placed on top of positive
  15738. * columns and below negative columns. In case of an inverted column
  15739. * chart or a bar chart the label is placed to the right of positive
  15740. * bars and to the left of negative bars.
  15741. *
  15742. * @product highcharts
  15743. */
  15744. stackLabels: {
  15745. /**
  15746. * Allow the stack labels to overlap.
  15747. *
  15748. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  15749. * Default false
  15750. *
  15751. * @since 5.0.13
  15752. * @product highcharts
  15753. */
  15754. allowOverlap: false,
  15755. /**
  15756. * Enable or disable the stack total labels.
  15757. *
  15758. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  15759. * Enabled stack total labels
  15760. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
  15761. * Enabled stack labels in waterfall chart
  15762. *
  15763. * @since 2.1.5
  15764. * @product highcharts
  15765. */
  15766. enabled: false,
  15767. /**
  15768. * Whether to hide stack labels that are outside the plot area.
  15769. * By default, the stack label is moved
  15770. * inside the plot area according to the
  15771. * [overflow](/highcharts/#yAxis/stackLabels/overflow)
  15772. * option.
  15773. *
  15774. * @type {boolean}
  15775. * @since 7.1.3
  15776. */
  15777. crop: true,
  15778. /**
  15779. * How to handle stack total labels that flow outside the plot area.
  15780. * The default is set to `"justify"`,
  15781. * which aligns them inside the plot area.
  15782. * For columns and bars, this means it will be moved inside the bar.
  15783. * To display stack labels outside the plot area,
  15784. * set `crop` to `false` and `overflow` to `"allow"`.
  15785. *
  15786. * @sample highcharts/yaxis/stacklabels-overflow/
  15787. * Stack labels flows outside the plot area.
  15788. *
  15789. * @type {Highcharts.DataLabelsOverflowValue}
  15790. * @since 7.1.3
  15791. */
  15792. overflow: 'justify',
  15793. /* eslint-disable valid-jsdoc */
  15794. /**
  15795. * Callback JavaScript function to format the label. The value is
  15796. * given by `this.total`.
  15797. *
  15798. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  15799. * Added units to stack total value
  15800. *
  15801. * @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
  15802. * @since 2.1.5
  15803. * @product highcharts
  15804. */
  15805. formatter: function () {
  15806. var numberFormatter = this.axis.chart.numberFormatter;
  15807. /* eslint-enable valid-jsdoc */
  15808. return numberFormatter(this.total, -1);
  15809. },
  15810. /**
  15811. * CSS styles for the label.
  15812. *
  15813. * In styled mode, the styles are set in the
  15814. * `.highcharts-stack-label` class.
  15815. *
  15816. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  15817. * Red stack total labels
  15818. *
  15819. * @type {Highcharts.CSSObject}
  15820. * @since 2.1.5
  15821. * @product highcharts
  15822. */
  15823. style: {
  15824. /** @internal */
  15825. color: '#000000',
  15826. /** @internal */
  15827. fontSize: '11px',
  15828. /** @internal */
  15829. fontWeight: 'bold',
  15830. /** @internal */
  15831. textOutline: '1px contrast'
  15832. }
  15833. },
  15834. gridLineWidth: 1,
  15835. lineWidth: 0
  15836. // tickWidth: 0
  15837. },
  15838. /**
  15839. * The Z axis or depth axis for 3D plots.
  15840. *
  15841. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  15842. * access to the axis.
  15843. *
  15844. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  15845. * Z-Axis with Categories
  15846. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  15847. * Z-Axis with styling
  15848. *
  15849. * @type {*|Array<*>}
  15850. * @extends xAxis
  15851. * @since 5.0.0
  15852. * @product highcharts
  15853. * @excluding breaks, crosshair, height, left, lineColor, lineWidth,
  15854. * nameToX, showEmpty, top, width
  15855. * @apioption zAxis
  15856. *
  15857. * @private
  15858. */
  15859. // This variable extends the defaultOptions for left axes.
  15860. defaultLeftAxisOptions: {
  15861. labels: {
  15862. x: -15
  15863. },
  15864. title: {
  15865. rotation: 270
  15866. }
  15867. },
  15868. // This variable extends the defaultOptions for right axes.
  15869. defaultRightAxisOptions: {
  15870. labels: {
  15871. x: 15
  15872. },
  15873. title: {
  15874. rotation: 90
  15875. }
  15876. },
  15877. // This variable extends the defaultOptions for bottom axes.
  15878. defaultBottomAxisOptions: {
  15879. labels: {
  15880. autoRotation: [-45],
  15881. x: 0
  15882. // overflow: undefined,
  15883. // staggerLines: null
  15884. },
  15885. margin: 15,
  15886. title: {
  15887. rotation: 0
  15888. }
  15889. },
  15890. // This variable extends the defaultOptions for top axes.
  15891. defaultTopAxisOptions: {
  15892. labels: {
  15893. autoRotation: [-45],
  15894. x: 0
  15895. // overflow: undefined
  15896. // staggerLines: null
  15897. },
  15898. margin: 15,
  15899. title: {
  15900. rotation: 0
  15901. }
  15902. },
  15903. /* eslint-disable no-invalid-this, valid-jsdoc */
  15904. /**
  15905. * Overrideable function to initialize the axis.
  15906. *
  15907. * @see {@link Axis}
  15908. *
  15909. * @function Highcharts.Axis#init
  15910. *
  15911. * @param {Highcharts.Chart} chart
  15912. *
  15913. * @param {Highcharts.AxisOptions} userOptions
  15914. *
  15915. * @return {void}
  15916. *
  15917. * @fires Highcharts.Axis#event:afterInit
  15918. * @fires Highcharts.Axis#event:init
  15919. */
  15920. init: function (chart, userOptions) {
  15921. var isXAxis = userOptions.isX, axis = this;
  15922. /**
  15923. * The Chart that the axis belongs to.
  15924. *
  15925. * @name Highcharts.Axis#chart
  15926. * @type {Highcharts.Chart}
  15927. */
  15928. axis.chart = chart;
  15929. /**
  15930. * Whether the axis is horizontal.
  15931. *
  15932. * @name Highcharts.Axis#horiz
  15933. * @type {boolean|undefined}
  15934. */
  15935. axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
  15936. /**
  15937. * Whether the axis is the x-axis.
  15938. *
  15939. * @name Highcharts.Axis#isXAxis
  15940. * @type {boolean|undefined}
  15941. */
  15942. axis.isXAxis = isXAxis;
  15943. /**
  15944. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  15945. * or `colorAxis`. Corresponds to properties on Chart, for example
  15946. * {@link Chart.xAxis}.
  15947. *
  15948. * @name Highcharts.Axis#coll
  15949. * @type {string}
  15950. */
  15951. axis.coll = axis.coll || (isXAxis ? 'xAxis' : 'yAxis');
  15952. fireEvent(this, 'init', { userOptions: userOptions });
  15953. axis.opposite = userOptions.opposite; // needed in setOptions
  15954. /**
  15955. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  15956. * is bottom and 3 is left.
  15957. *
  15958. * @name Highcharts.Axis#side
  15959. * @type {number}
  15960. */
  15961. axis.side = userOptions.side || (axis.horiz ?
  15962. (axis.opposite ? 0 : 2) : // top : bottom
  15963. (axis.opposite ? 1 : 3)); // right : left
  15964. /**
  15965. * Current options for the axis after merge of defaults and user's
  15966. * options.
  15967. *
  15968. * @name Highcharts.Axis#options
  15969. * @type {Highcharts.AxisOptions}
  15970. */
  15971. axis.setOptions(userOptions);
  15972. var options = this.options, type = options.type, isDatetimeAxis = type === 'datetime';
  15973. axis.labelFormatter = options.labels.formatter ||
  15974. // can be overwritten by dynamic format
  15975. axis.defaultLabelFormatter;
  15976. /**
  15977. * User's options for this axis without defaults.
  15978. *
  15979. * @name Highcharts.Axis#userOptions
  15980. * @type {Highcharts.AxisOptions}
  15981. */
  15982. axis.userOptions = userOptions;
  15983. axis.minPixelPadding = 0;
  15984. /**
  15985. * Whether the axis is reversed. Based on the `axis.reversed`,
  15986. * option, but inverted charts have reversed xAxis by default.
  15987. *
  15988. * @name Highcharts.Axis#reversed
  15989. * @type {boolean}
  15990. */
  15991. axis.reversed = options.reversed;
  15992. axis.visible = options.visible !== false;
  15993. axis.zoomEnabled = options.zoomEnabled !== false;
  15994. // Initial categories
  15995. axis.hasNames =
  15996. type === 'category' || options.categories === true;
  15997. /**
  15998. * If categories are present for the axis, names are used instead of
  15999. * numbers for that axis.
  16000. *
  16001. * Since Highcharts 3.0, categories can also be extracted by giving each
  16002. * point a name and setting axis type to `category`. However, if you
  16003. * have multiple series, best practice remains defining the `categories`
  16004. * array.
  16005. *
  16006. * @see [xAxis.categories](/highcharts/xAxis.categories)
  16007. *
  16008. * @name Highcharts.Axis#categories
  16009. * @type {Array<string>}
  16010. * @readonly
  16011. */
  16012. axis.categories = options.categories || axis.hasNames;
  16013. if (!axis.names) { // Preserve on update (#3830)
  16014. axis.names = [];
  16015. axis.names.keys = {};
  16016. }
  16017. // Placeholder for plotlines and plotbands groups
  16018. axis.plotLinesAndBandsGroups = {};
  16019. // Shorthand types
  16020. axis.isLog = type === 'logarithmic';
  16021. axis.isDatetimeAxis = isDatetimeAxis;
  16022. axis.positiveValuesOnly = axis.isLog && !axis.allowNegativeLog;
  16023. // Flag, if axis is linked to another axis
  16024. axis.isLinked = defined(options.linkedTo);
  16025. /**
  16026. * List of major ticks mapped by postition on axis.
  16027. *
  16028. * @see {@link Highcharts.Tick}
  16029. *
  16030. * @name Highcharts.Axis#ticks
  16031. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  16032. */
  16033. axis.ticks = {};
  16034. axis.labelEdge = [];
  16035. /**
  16036. * List of minor ticks mapped by position on the axis.
  16037. *
  16038. * @see {@link Highcharts.Tick}
  16039. *
  16040. * @name Highcharts.Axis#minorTicks
  16041. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  16042. */
  16043. axis.minorTicks = {};
  16044. // List of plotLines/Bands
  16045. axis.plotLinesAndBands = [];
  16046. // Alternate bands
  16047. axis.alternateBands = {};
  16048. // Axis metrics
  16049. axis.len = 0;
  16050. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  16051. axis.range = options.range;
  16052. axis.offset = options.offset || 0;
  16053. // Dictionary for stacks
  16054. axis.stacks = {};
  16055. axis.oldStacks = {};
  16056. axis.stacksTouched = 0;
  16057. /**
  16058. * The maximum value of the axis. In a logarithmic axis, this is the
  16059. * logarithm of the real value, and the real value can be obtained from
  16060. * {@link Axis#getExtremes}.
  16061. *
  16062. * @name Highcharts.Axis#max
  16063. * @type {number|null}
  16064. */
  16065. axis.max = null;
  16066. /**
  16067. * The minimum value of the axis. In a logarithmic axis, this is the
  16068. * logarithm of the real value, and the real value can be obtained from
  16069. * {@link Axis#getExtremes}.
  16070. *
  16071. * @name Highcharts.Axis#min
  16072. * @type {number|null}
  16073. */
  16074. axis.min = null;
  16075. /**
  16076. * The processed crosshair options.
  16077. *
  16078. * @name Highcharts.Axis#crosshair
  16079. * @type {boolean|Highcharts.AxisCrosshairOptions}
  16080. */
  16081. axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false);
  16082. var events = axis.options.events;
  16083. // Register. Don't add it again on Axis.update().
  16084. if (chart.axes.indexOf(axis) === -1) { //
  16085. if (isXAxis) { // #2713
  16086. chart.axes.splice(chart.xAxis.length, 0, axis);
  16087. }
  16088. else {
  16089. chart.axes.push(axis);
  16090. }
  16091. chart[axis.coll].push(axis);
  16092. }
  16093. /**
  16094. * All series associated to the axis.
  16095. *
  16096. * @name Highcharts.Axis#series
  16097. * @type {Array<Highcharts.Series>}
  16098. */
  16099. axis.series = axis.series || []; // populated by Series
  16100. // Reversed axis
  16101. if (chart.inverted &&
  16102. !axis.isZAxis &&
  16103. isXAxis &&
  16104. typeof axis.reversed === 'undefined') {
  16105. axis.reversed = true;
  16106. }
  16107. // register event listeners
  16108. objectEach(events, function (event, eventType) {
  16109. if (H.isFunction(event)) {
  16110. addEvent(axis, eventType, event);
  16111. }
  16112. });
  16113. // extend logarithmic axis
  16114. axis.lin2log = options.linearToLogConverter || axis.lin2log;
  16115. if (axis.isLog) {
  16116. axis.val2lin = axis.log2lin;
  16117. axis.lin2val = axis.lin2log;
  16118. }
  16119. fireEvent(this, 'afterInit');
  16120. },
  16121. /**
  16122. * Merge and set options.
  16123. *
  16124. * @private
  16125. * @function Highcharts.Axis#setOptions
  16126. * @param {Highcharts.AxisOptions} userOptions
  16127. * @return {void}
  16128. * @fires Highcharts.Axis#event:afterSetOptions
  16129. */
  16130. setOptions: function (userOptions) {
  16131. this.options = merge(this.defaultOptions, (this.coll === 'yAxis') && this.defaultYAxisOptions, [
  16132. this.defaultTopAxisOptions,
  16133. this.defaultRightAxisOptions,
  16134. this.defaultBottomAxisOptions,
  16135. this.defaultLeftAxisOptions
  16136. ][this.side], merge(
  16137. // if set in setOptions (#1053):
  16138. defaultOptions[this.coll], userOptions));
  16139. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  16140. },
  16141. /**
  16142. * The default label formatter. The context is a special config object for
  16143. * the label. In apps, use the
  16144. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  16145. * instead, except when a modification is needed.
  16146. *
  16147. * @function Highcharts.Axis#defaultLabelFormatter
  16148. *
  16149. * @this Highcharts.AxisLabelsFormatterContextObject
  16150. *
  16151. * @return {string}
  16152. * The formatted label content.
  16153. */
  16154. defaultLabelFormatter: function () {
  16155. var axis = this.axis, value = this.value, 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,
  16156. // make sure the same symbol is added for all labels on a linear
  16157. // axis
  16158. numericSymbolDetector = axis.isLog ?
  16159. Math.abs(value) :
  16160. axis.tickInterval;
  16161. var chart = this.chart;
  16162. var numberFormatter = chart.numberFormatter;
  16163. if (formatOption) {
  16164. ret = format(formatOption, this, chart);
  16165. }
  16166. else if (categories) {
  16167. ret = value;
  16168. }
  16169. else if (dateTimeLabelFormat) { // datetime axis
  16170. ret = time.dateFormat(dateTimeLabelFormat, value);
  16171. }
  16172. else if (i && numericSymbolDetector >= 1000) {
  16173. // Decide whether we should add a numeric symbol like k (thousands)
  16174. // or M (millions). If we are to enable this in tooltip or other
  16175. // places as well, we can move this logic to the numberFormatter and
  16176. // enable it by a parameter.
  16177. while (i-- && typeof ret === 'undefined') {
  16178. multi = Math.pow(numSymMagnitude, i + 1);
  16179. if (
  16180. // Only accept a numeric symbol when the distance is more
  16181. // than a full unit. So for example if the symbol is k, we
  16182. // don't accept numbers like 0.5k.
  16183. numericSymbolDetector >= multi &&
  16184. // Accept one decimal before the symbol. Accepts 0.5k but
  16185. // not 0.25k. How does this work with the previous?
  16186. (value * 10) % multi === 0 &&
  16187. numericSymbols[i] !== null &&
  16188. value !== 0) { // #5480
  16189. ret = numberFormatter(value / multi, -1) +
  16190. numericSymbols[i];
  16191. }
  16192. }
  16193. }
  16194. if (typeof ret === 'undefined') {
  16195. if (Math.abs(value) >= 10000) { // add thousands separators
  16196. ret = numberFormatter(value, -1);
  16197. }
  16198. else { // small numbers
  16199. ret = numberFormatter(value, -1, void 0, ''); // #2466
  16200. }
  16201. }
  16202. return ret;
  16203. },
  16204. /**
  16205. * Get the minimum and maximum for the series of each axis. The function
  16206. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  16207. * @private
  16208. * @fires Highcharts.Axis#event:afterGetSeriesExtremes
  16209. * @fires Highcharts.Axis#event:getSeriesExtremes
  16210. */
  16211. getSeriesExtremes: function () {
  16212. var axis = this, chart = axis.chart, xExtremes;
  16213. fireEvent(this, 'getSeriesExtremes', null, function () {
  16214. axis.hasVisibleSeries = false;
  16215. // Reset properties in case we're redrawing (#3353)
  16216. axis.dataMin = axis.dataMax = axis.threshold = null;
  16217. axis.softThreshold = !axis.isXAxis;
  16218. if (axis.buildStacks) {
  16219. axis.buildStacks();
  16220. }
  16221. // loop through this axis' series
  16222. axis.series.forEach(function (series) {
  16223. if (series.visible ||
  16224. !chart.options.chart.ignoreHiddenSeries) {
  16225. var seriesOptions = series.options, xData, threshold = seriesOptions.threshold, seriesDataMin, seriesDataMax;
  16226. axis.hasVisibleSeries = true;
  16227. // Validate threshold in logarithmic axes
  16228. if (axis.positiveValuesOnly && threshold <= 0) {
  16229. threshold = null;
  16230. }
  16231. // Get dataMin and dataMax for X axes
  16232. if (axis.isXAxis) {
  16233. xData = series.xData;
  16234. if (xData.length) {
  16235. xExtremes = series.getXExtremes(xData);
  16236. // If xData contains values which is not numbers,
  16237. // then filter them out. To prevent performance hit,
  16238. // we only do this after we have already found
  16239. // seriesDataMin because in most cases all data is
  16240. // valid. #5234.
  16241. seriesDataMin = xExtremes.min;
  16242. seriesDataMax = xExtremes.max;
  16243. if (!isNumber(seriesDataMin) &&
  16244. // #5010:
  16245. !(seriesDataMin instanceof Date)) {
  16246. xData = xData.filter(isNumber);
  16247. xExtremes = series.getXExtremes(xData);
  16248. // Do it again with valid data
  16249. seriesDataMin = xExtremes.min;
  16250. seriesDataMax = xExtremes.max;
  16251. }
  16252. if (xData.length) {
  16253. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  16254. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  16255. }
  16256. }
  16257. // Get dataMin and dataMax for Y axes, as well as handle
  16258. // stacking and processed data
  16259. }
  16260. else {
  16261. // Get this particular series extremes
  16262. series.getExtremes();
  16263. seriesDataMax = series.dataMax;
  16264. seriesDataMin = series.dataMin;
  16265. // Get the dataMin and dataMax so far. If percentage is
  16266. // used, the min and max are always 0 and 100. If
  16267. // seriesDataMin and seriesDataMax is null, then series
  16268. // doesn't have active y data, we continue with nulls
  16269. if (defined(seriesDataMin) && defined(seriesDataMax)) {
  16270. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  16271. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  16272. }
  16273. // Adjust to threshold
  16274. if (defined(threshold)) {
  16275. axis.threshold = threshold;
  16276. }
  16277. // If any series has a hard threshold, it takes
  16278. // precedence
  16279. if (!seriesOptions.softThreshold ||
  16280. axis.positiveValuesOnly) {
  16281. axis.softThreshold = false;
  16282. }
  16283. }
  16284. }
  16285. });
  16286. });
  16287. fireEvent(this, 'afterGetSeriesExtremes');
  16288. },
  16289. /**
  16290. * Translate from axis value to pixel position on the chart, or back. Use
  16291. * the `toPixels` and `toValue` functions in applications.
  16292. * @private
  16293. * @return {number|undefined}
  16294. */
  16295. translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
  16296. var axis = this.linkedParent || this, // #1417
  16297. sign = 1, cvsOffset = 0, localA = old ? axis.oldTransA : axis.transA, localMin = old ? axis.oldMin : axis.min, returnValue = 0, minPixelPadding = axis.minPixelPadding, doPostTranslate = (axis.isOrdinal ||
  16298. axis.isBroken ||
  16299. (axis.isLog && handleLog)) && axis.lin2val;
  16300. if (!localA) {
  16301. localA = axis.transA;
  16302. }
  16303. // In vertical axes, the canvas coordinates start from 0 at the top like
  16304. // in SVG.
  16305. if (cvsCoord) {
  16306. sign *= -1; // canvas coordinates inverts the value
  16307. cvsOffset = axis.len;
  16308. }
  16309. // Handle reversed axis
  16310. if (axis.reversed) {
  16311. sign *= -1;
  16312. cvsOffset -= sign * (axis.sector || axis.len);
  16313. }
  16314. // From pixels to value
  16315. if (backwards) { // reverse translation
  16316. val = val * sign + cvsOffset;
  16317. val -= minPixelPadding;
  16318. // from chart pixel to value:
  16319. returnValue = val / localA + localMin;
  16320. if (doPostTranslate) { // log and ordinal axes
  16321. returnValue = axis.lin2val(returnValue);
  16322. }
  16323. // From value to pixels
  16324. }
  16325. else {
  16326. if (doPostTranslate) { // log and ordinal axes
  16327. val = axis.val2lin(val);
  16328. }
  16329. returnValue = isNumber(localMin) ?
  16330. (sign * (val - localMin) * localA +
  16331. cvsOffset +
  16332. (sign * minPixelPadding) +
  16333. (isNumber(pointPlacement) ?
  16334. localA * pointPlacement :
  16335. 0)) :
  16336. void 0;
  16337. }
  16338. return returnValue;
  16339. },
  16340. /**
  16341. * Translate a value in terms of axis units into pixels within the chart.
  16342. *
  16343. * @function Highcharts.Axis#toPixels
  16344. *
  16345. * @param {number} value
  16346. * A value in terms of axis units.
  16347. *
  16348. * @param {boolean} paneCoordinates
  16349. * Whether to return the pixel coordinate relative to the chart or
  16350. * just the axis/pane itself.
  16351. *
  16352. * @return {number}
  16353. * Pixel position of the value on the chart or axis.
  16354. */
  16355. toPixels: function (value, paneCoordinates) {
  16356. return this.translate(value, false, !this.horiz, null, true) +
  16357. (paneCoordinates ? 0 : this.pos);
  16358. },
  16359. /**
  16360. * Translate a pixel position along the axis to a value in terms of axis
  16361. * units.
  16362. *
  16363. * @function Highcharts.Axis#toValue
  16364. *
  16365. * @param {number} pixel
  16366. * The pixel value coordinate.
  16367. *
  16368. * @param {boolean} [paneCoordiantes=false]
  16369. * Whether the input pixel is relative to the chart or just the
  16370. * axis/pane itself.
  16371. *
  16372. * @return {number}
  16373. * The axis value.
  16374. */
  16375. toValue: function (pixel, paneCoordinates) {
  16376. return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
  16377. },
  16378. /**
  16379. * Create the path for a plot line that goes from the given value on
  16380. * this axis, across the plot to the opposite side. Also used internally for
  16381. * grid lines and crosshairs.
  16382. *
  16383. * @function Highcharts.Axis#getPlotLinePath
  16384. *
  16385. * @param {Highcharts.AxisPlotLinePathOptionsObject} options
  16386. * Options for the path.
  16387. *
  16388. * @return {Highcharts.SVGPathArray|null}
  16389. * The SVG path definition for the plot line.
  16390. */
  16391. getPlotLinePath: function (options) {
  16392. 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,
  16393. /**
  16394. * Check if x is between a and b. If not, either move to a/b
  16395. * or skip, depending on the force parameter.
  16396. */
  16397. between = function (x, a, b) {
  16398. if (force !== 'pass' && x < a || x > b) {
  16399. if (force) {
  16400. x = clamp(x, a, b);
  16401. }
  16402. else {
  16403. skip = true;
  16404. }
  16405. }
  16406. return x;
  16407. };
  16408. evt = {
  16409. value: value,
  16410. lineWidth: lineWidth,
  16411. old: old,
  16412. force: force,
  16413. acrossPanes: options.acrossPanes,
  16414. translatedValue: translatedValue
  16415. };
  16416. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  16417. translatedValue = pick(translatedValue, axis.translate(value, null, null, old));
  16418. // Keep the translated value within sane bounds, and avoid Infinity
  16419. // to fail the isNumber test (#7709).
  16420. translatedValue = clamp(translatedValue, -1e5, 1e5);
  16421. x1 = x2 = Math.round(translatedValue + transB);
  16422. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  16423. if (!isNumber(translatedValue)) { // no min or max
  16424. skip = true;
  16425. force = false; // #7175, don't force it when path is invalid
  16426. }
  16427. else if (axis.horiz) {
  16428. y1 = axisTop;
  16429. y2 = cHeight - axis.bottom;
  16430. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  16431. }
  16432. else {
  16433. x1 = axisLeft;
  16434. x2 = cWidth - axis.right;
  16435. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  16436. }
  16437. e.path = skip && !force ?
  16438. null :
  16439. chart.renderer.crispLine(['M', x1, y1, 'L', x2, y2], lineWidth || 1);
  16440. });
  16441. return evt.path;
  16442. },
  16443. /**
  16444. * Internal function to et the tick positions of a linear axis to round
  16445. * values like whole tens or every five.
  16446. *
  16447. * @function Highcharts.Axis#getLinearTickPositions
  16448. *
  16449. * @param {number} tickInterval
  16450. * The normalized tick interval.
  16451. *
  16452. * @param {number} min
  16453. * Axis minimum.
  16454. *
  16455. * @param {number} max
  16456. * Axis maximum.
  16457. *
  16458. * @return {Array<number>}
  16459. * An array of axis values where ticks should be placed.
  16460. */
  16461. getLinearTickPositions: function (tickInterval, min, max) {
  16462. var pos, lastPos, roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval), roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval), tickPositions = [], precision;
  16463. // When the precision is higher than what we filter out in
  16464. // correctFloat, skip it (#6183).
  16465. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  16466. precision = 20;
  16467. }
  16468. // For single points, add a tick regardless of the relative position
  16469. // (#2662, #6274)
  16470. if (this.single) {
  16471. return [min];
  16472. }
  16473. // Populate the intermediate values
  16474. pos = roundedMin;
  16475. while (pos <= roundedMax) {
  16476. // Place the tick on the rounded value
  16477. tickPositions.push(pos);
  16478. // Always add the raw tickInterval, not the corrected one.
  16479. pos = correctFloat(pos + tickInterval, precision);
  16480. // If the interval is not big enough in the current min - max range
  16481. // to actually increase the loop variable, we need to break out to
  16482. // prevent endless loop. Issue #619
  16483. if (pos === lastPos) {
  16484. break;
  16485. }
  16486. // Record the last value
  16487. lastPos = pos;
  16488. }
  16489. return tickPositions;
  16490. },
  16491. /**
  16492. * Resolve the new minorTicks/minorTickInterval options into the legacy
  16493. * loosely typed minorTickInterval option.
  16494. *
  16495. * @function Highcharts.Axis#getMinorTickInterval
  16496. * @return {number|"auto"|null}
  16497. */
  16498. getMinorTickInterval: function () {
  16499. var options = this.options;
  16500. if (options.minorTicks === true) {
  16501. return pick(options.minorTickInterval, 'auto');
  16502. }
  16503. if (options.minorTicks === false) {
  16504. return null;
  16505. }
  16506. return options.minorTickInterval;
  16507. },
  16508. /**
  16509. * Internal function to return the minor tick positions. For logarithmic
  16510. * axes, the same logic as for major ticks is reused.
  16511. *
  16512. * @function Highcharts.Axis#getMinorTickPositions
  16513. *
  16514. * @return {Array<number>}
  16515. * An array of axis values where ticks should be placed.
  16516. */
  16517. getMinorTickPositions: function () {
  16518. var axis = this, options = axis.options, tickPositions = axis.tickPositions, minorTickInterval = axis.minorTickInterval, minorTickPositions = [], pos, pointRangePadding = axis.pointRangePadding || 0, min = axis.min - pointRangePadding, // #1498
  16519. max = axis.max + pointRangePadding, // #1498
  16520. range = max - min;
  16521. // If minor ticks get too dense, they are hard to read, and may cause
  16522. // long running script. So we don't draw them.
  16523. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  16524. if (axis.isLog) {
  16525. // For each interval in the major ticks, compute the minor ticks
  16526. // separately.
  16527. this.paddedTicks.forEach(function (pos, i, paddedTicks) {
  16528. if (i) {
  16529. minorTickPositions.push.apply(minorTickPositions, axis.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
  16530. }
  16531. });
  16532. }
  16533. else if (axis.isDatetimeAxis &&
  16534. this.getMinorTickInterval() === 'auto') { // #1314
  16535. minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
  16536. }
  16537. else {
  16538. for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
  16539. // Very, very, tight grid lines (#5771)
  16540. if (pos === minorTickPositions[0]) {
  16541. break;
  16542. }
  16543. minorTickPositions.push(pos);
  16544. }
  16545. }
  16546. }
  16547. if (minorTickPositions.length !== 0) {
  16548. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  16549. }
  16550. return minorTickPositions;
  16551. },
  16552. /**
  16553. * Adjust the min and max for the minimum range. Keep in mind that the
  16554. * series data is not yet processed, so we don't have information on data
  16555. * cropping and grouping, or updated `axis.pointRange` or
  16556. * `series.pointRange`. The data can't be processed until we have finally
  16557. * established min and max.
  16558. * @private
  16559. */
  16560. adjustForMinRange: function () {
  16561. var axis = this, options = axis.options, min = axis.min, max = axis.max, zoomOffset, spaceAvailable, closestDataRange, i, distance, xData, loopLength, minArgs, maxArgs, minRange;
  16562. // Set the automatic minimum range based on the closest point distance
  16563. if (axis.isXAxis &&
  16564. typeof axis.minRange === 'undefined' &&
  16565. !axis.isLog) {
  16566. if (defined(options.min) || defined(options.max)) {
  16567. axis.minRange = null; // don't do this again
  16568. }
  16569. else {
  16570. // Find the closest distance between raw data points, as opposed
  16571. // to closestPointRange that applies to processed points
  16572. // (cropped and grouped)
  16573. axis.series.forEach(function (series) {
  16574. xData = series.xData;
  16575. loopLength = series.xIncrement ? 1 : xData.length - 1;
  16576. for (i = loopLength; i > 0; i--) {
  16577. distance = xData[i] - xData[i - 1];
  16578. if (typeof closestDataRange === 'undefined' ||
  16579. distance < closestDataRange) {
  16580. closestDataRange = distance;
  16581. }
  16582. }
  16583. });
  16584. axis.minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
  16585. }
  16586. }
  16587. // if minRange is exceeded, adjust
  16588. if (max - min < axis.minRange) {
  16589. spaceAvailable =
  16590. axis.dataMax - axis.dataMin >=
  16591. axis.minRange;
  16592. minRange = axis.minRange;
  16593. zoomOffset = (minRange - max + min) / 2;
  16594. // if min and max options have been set, don't go beyond it
  16595. minArgs = [
  16596. min - zoomOffset,
  16597. pick(options.min, min - zoomOffset)
  16598. ];
  16599. // If space is available, stay within the data range
  16600. if (spaceAvailable) {
  16601. minArgs[2] = axis.isLog ?
  16602. axis.log2lin(axis.dataMin) :
  16603. axis.dataMin;
  16604. }
  16605. min = arrayMax(minArgs);
  16606. maxArgs = [
  16607. min + minRange,
  16608. pick(options.max, min + minRange)
  16609. ];
  16610. // If space is availabe, stay within the data range
  16611. if (spaceAvailable) {
  16612. maxArgs[2] = axis.isLog ?
  16613. axis.log2lin(axis.dataMax) :
  16614. axis.dataMax;
  16615. }
  16616. max = arrayMin(maxArgs);
  16617. // now if the max is adjusted, adjust the min back
  16618. if (max - min < minRange) {
  16619. minArgs[0] = max - minRange;
  16620. minArgs[1] = pick(options.min, max - minRange);
  16621. min = arrayMax(minArgs);
  16622. }
  16623. }
  16624. // Record modified extremes
  16625. axis.min = min;
  16626. axis.max = max;
  16627. },
  16628. /**
  16629. * Find the closestPointRange across all series.
  16630. * @private
  16631. */
  16632. getClosest: function () {
  16633. var ret;
  16634. if (this.categories) {
  16635. ret = 1;
  16636. }
  16637. else {
  16638. this.series.forEach(function (series) {
  16639. var seriesClosest = series.closestPointRange, visible = series.visible ||
  16640. !series.chart.options.chart.ignoreHiddenSeries;
  16641. if (!series.noSharedTooltip &&
  16642. defined(seriesClosest) &&
  16643. visible) {
  16644. ret = defined(ret) ?
  16645. Math.min(ret, seriesClosest) :
  16646. seriesClosest;
  16647. }
  16648. });
  16649. }
  16650. return ret;
  16651. },
  16652. /**
  16653. * When a point name is given and no x, search for the name in the existing
  16654. * categories, or if categories aren't provided, search names or create a
  16655. * new category (#2522).
  16656. * @private
  16657. * @param {Highcharts.Point} point The point to inspect.
  16658. * @return {number} The X value that the point is given.
  16659. */
  16660. nameToX: function (point) {
  16661. var explicitCategories = isArray(this.categories), names = explicitCategories ? this.categories : this.names, nameX = point.options.x, x;
  16662. point.series.requireSorting = false;
  16663. if (!defined(nameX)) {
  16664. nameX = this.options.uniqueNames === false ?
  16665. point.series.autoIncrement() :
  16666. (explicitCategories ?
  16667. names.indexOf(point.name) :
  16668. pick(names.keys[point.name], -1));
  16669. }
  16670. if (nameX === -1) { // Not found in currenct categories
  16671. if (!explicitCategories) {
  16672. x = names.length;
  16673. }
  16674. }
  16675. else {
  16676. x = nameX;
  16677. }
  16678. // Write the last point's name to the names array
  16679. if (typeof x !== 'undefined') {
  16680. this.names[x] = point.name;
  16681. // Backwards mapping is much faster than array searching (#7725)
  16682. this.names.keys[point.name] = x;
  16683. }
  16684. return x;
  16685. },
  16686. /**
  16687. * When changes have been done to series data, update the axis.names.
  16688. * @private
  16689. */
  16690. updateNames: function () {
  16691. var axis = this, names = this.names, i = names.length;
  16692. if (i > 0) {
  16693. Object.keys(names.keys).forEach(function (key) {
  16694. delete (names.keys)[key];
  16695. });
  16696. names.length = 0;
  16697. this.minRange = this.userMinRange; // Reset
  16698. (this.series || []).forEach(function (series) {
  16699. // Reset incrementer (#5928)
  16700. series.xIncrement = null;
  16701. // When adding a series, points are not yet generated
  16702. if (!series.points || series.isDirtyData) {
  16703. // When we're updating the series with data that is longer
  16704. // than it was, and cropThreshold is passed, we need to make
  16705. // sure that the axis.max is increased _before_ running the
  16706. // premature processData. Otherwise this early iteration of
  16707. // processData will crop the points to axis.max, and the
  16708. // names array will be too short (#5857).
  16709. axis.max = Math.max(axis.max, series.xData.length - 1);
  16710. series.processData();
  16711. series.generatePoints();
  16712. }
  16713. series.data.forEach(function (point, i) {
  16714. var x;
  16715. if (point &&
  16716. point.options &&
  16717. typeof point.name !== 'undefined' // #9562
  16718. ) {
  16719. x = axis.nameToX(point);
  16720. if (typeof x !== 'undefined' && x !== point.x) {
  16721. point.x = x;
  16722. series.xData[i] = x;
  16723. }
  16724. }
  16725. });
  16726. });
  16727. }
  16728. },
  16729. /**
  16730. * Update translation information.
  16731. * @private
  16732. * @param {boolean} [saveOld]
  16733. * @fires Highcharts.Axis#event:afterSetAxisTranslation
  16734. */
  16735. setAxisTranslation: function (saveOld) {
  16736. 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;
  16737. // Adjust translation for padding. Y axis with categories need to go
  16738. // through the same (#1784).
  16739. if (isXAxis || hasCategories || pointRange) {
  16740. // Get the closest points
  16741. closestPointRange = axis.getClosest();
  16742. if (linkedParent) {
  16743. minPointOffset = linkedParent.minPointOffset;
  16744. pointRangePadding = linkedParent.pointRangePadding;
  16745. }
  16746. else {
  16747. axis.series.forEach(function (series) {
  16748. var seriesPointRange = hasCategories ?
  16749. 1 :
  16750. (isXAxis ?
  16751. pick(series.options.pointRange, closestPointRange, 0) :
  16752. (axis.axisPointRange || 0)), // #2806
  16753. pointPlacement = series.options.pointPlacement;
  16754. pointRange = Math.max(pointRange, seriesPointRange);
  16755. if (!axis.single || hasCategories) {
  16756. // TODO: series should internally set x- and y-
  16757. // pointPlacement to simplify this logic.
  16758. var isPointPlacementAxis = (seriesTypes.xrange &&
  16759. series instanceof seriesTypes.xrange) ? !isXAxis : isXAxis;
  16760. // minPointOffset is the value padding to the left of
  16761. // the axis in order to make room for points with a
  16762. // pointRange, typically columns. When the
  16763. // pointPlacement option is 'between' or 'on', this
  16764. // padding does not apply.
  16765. minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && isString(pointPlacement) ?
  16766. 0 :
  16767. seriesPointRange / 2);
  16768. // Determine the total padding needed to the length of
  16769. // the axis to make room for the pointRange. If the
  16770. // series' pointPlacement is 'on', no padding is added.
  16771. pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
  16772. 0 :
  16773. seriesPointRange);
  16774. }
  16775. });
  16776. }
  16777. // Record minPointOffset and pointRangePadding
  16778. ordinalCorrection = axis.ordinalSlope && closestPointRange ?
  16779. axis.ordinalSlope / closestPointRange :
  16780. 1; // #988, #1853
  16781. axis.minPointOffset = minPointOffset =
  16782. minPointOffset * ordinalCorrection;
  16783. axis.pointRangePadding =
  16784. pointRangePadding = pointRangePadding * ordinalCorrection;
  16785. // pointRange means the width reserved for each point, like in a
  16786. // column chart
  16787. axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
  16788. // closestPointRange means the closest distance between points. In
  16789. // columns it is mostly equal to pointRange, but in lines pointRange
  16790. // is 0 while closestPointRange is some other value
  16791. if (isXAxis) {
  16792. axis.closestPointRange = closestPointRange;
  16793. }
  16794. }
  16795. // Secondary values
  16796. if (saveOld) {
  16797. axis.oldTransA = transA;
  16798. }
  16799. axis.translationSlope = axis.transA = transA =
  16800. axis.staticScale ||
  16801. axis.len / ((range + pointRangePadding) || 1);
  16802. // Translation addend
  16803. axis.transB = axis.horiz ? axis.left : axis.bottom;
  16804. axis.minPixelPadding = transA * minPointOffset;
  16805. fireEvent(this, 'afterSetAxisTranslation');
  16806. },
  16807. /**
  16808. * @private
  16809. * @return {number}
  16810. */
  16811. minFromRange: function () {
  16812. return this.max - this.range;
  16813. },
  16814. /**
  16815. * Set the tick positions to round values and optionally extend the extremes
  16816. * to the nearest tick.
  16817. * @private
  16818. * @param {boolean} secondPass
  16819. * @fires Highcharts.Axis#event:foundExtremes
  16820. */
  16821. setTickInterval: function (secondPass) {
  16822. var axis = this, chart = axis.chart, options = axis.options, isLog = axis.isLog, isDatetimeAxis = axis.isDatetimeAxis, 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;
  16823. if (!isDatetimeAxis && !categories && !isLinked) {
  16824. this.getTickAmount();
  16825. }
  16826. // Min or max set either by zooming/setExtremes or initial options
  16827. hardMin = pick(axis.userMin, options.min);
  16828. hardMax = pick(axis.userMax, options.max);
  16829. // Linked axis gets the extremes from the parent axis
  16830. if (isLinked) {
  16831. axis.linkedParent = chart[axis.coll][options.linkedTo];
  16832. linkedParentExtremes = axis.linkedParent.getExtremes();
  16833. axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
  16834. axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
  16835. if (options.type !== axis.linkedParent.options.type) {
  16836. // Can't link axes of different type
  16837. H.error(11, 1, chart);
  16838. }
  16839. // Initial min and max from the extreme data values
  16840. }
  16841. else {
  16842. // Adjust to hard threshold
  16843. if (!softThreshold && defined(threshold)) {
  16844. if (axis.dataMin >= threshold) {
  16845. thresholdMin = threshold;
  16846. minPadding = 0;
  16847. }
  16848. else if (axis.dataMax <= threshold) {
  16849. thresholdMax = threshold;
  16850. maxPadding = 0;
  16851. }
  16852. }
  16853. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  16854. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  16855. }
  16856. if (isLog) {
  16857. if (axis.positiveValuesOnly &&
  16858. !secondPass &&
  16859. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
  16860. // Can't plot negative values on log axis
  16861. H.error(10, 1, chart);
  16862. }
  16863. // The correctFloat cures #934, float errors on full tens. But it
  16864. // was too aggressive for #4360 because of conversion back to lin,
  16865. // therefore use precision 15.
  16866. axis.min = correctFloat(axis.log2lin(axis.min), 16);
  16867. axis.max = correctFloat(axis.log2lin(axis.max), 16);
  16868. }
  16869. // handle zoomed range
  16870. if (axis.range && defined(axis.max)) {
  16871. // #618, #6773:
  16872. axis.userMin = axis.min = hardMin =
  16873. Math.max(axis.dataMin, axis.minFromRange());
  16874. axis.userMax = hardMax = axis.max;
  16875. axis.range = null; // don't use it when running setExtremes
  16876. }
  16877. // Hook for Highstock Scroller. Consider combining with beforePadding.
  16878. fireEvent(axis, 'foundExtremes');
  16879. // Hook for adjusting this.min and this.max. Used by bubble series.
  16880. if (axis.beforePadding) {
  16881. axis.beforePadding();
  16882. }
  16883. // adjust min and max for the minimum range
  16884. axis.adjustForMinRange();
  16885. // Pad the values to get clear of the chart's edges. To avoid
  16886. // tickInterval taking the padding into account, we do this after
  16887. // computing tick interval (#1337).
  16888. if (!categories &&
  16889. !axis.axisPointRange &&
  16890. !axis.usePercentage &&
  16891. !isLinked &&
  16892. defined(axis.min) &&
  16893. defined(axis.max)) {
  16894. length = axis.max - axis.min;
  16895. if (length) {
  16896. if (!defined(hardMin) && minPadding) {
  16897. axis.min -= length * minPadding;
  16898. }
  16899. if (!defined(hardMax) && maxPadding) {
  16900. axis.max += length * maxPadding;
  16901. }
  16902. }
  16903. }
  16904. // Handle options for floor, ceiling, softMin and softMax (#6359)
  16905. if (!isNumber(axis.userMin)) {
  16906. if (isNumber(options.softMin) && options.softMin < axis.min) {
  16907. axis.min = hardMin = options.softMin; // #6894
  16908. }
  16909. if (isNumber(options.floor)) {
  16910. axis.min = Math.max(axis.min, options.floor);
  16911. }
  16912. }
  16913. if (!isNumber(axis.userMax)) {
  16914. if (isNumber(options.softMax) && options.softMax > axis.max) {
  16915. axis.max = hardMax = options.softMax; // #6894
  16916. }
  16917. if (isNumber(options.ceiling)) {
  16918. axis.max = Math.min(axis.max, options.ceiling);
  16919. }
  16920. }
  16921. // When the threshold is soft, adjust the extreme value only if the data
  16922. // extreme and the padded extreme land on either side of the threshold.
  16923. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  16924. // for -1 because of the default minPadding and startOnTick options.
  16925. // This is prevented by the softThreshold option.
  16926. if (softThreshold && defined(axis.dataMin)) {
  16927. threshold = threshold || 0;
  16928. if (!defined(hardMin) &&
  16929. axis.min < threshold &&
  16930. axis.dataMin >= threshold) {
  16931. axis.min = axis.options.minRange ?
  16932. Math.min(threshold, axis.max -
  16933. axis.minRange) :
  16934. threshold;
  16935. }
  16936. else if (!defined(hardMax) &&
  16937. axis.max > threshold &&
  16938. axis.dataMax <= threshold) {
  16939. axis.max = axis.options.minRange ?
  16940. Math.max(threshold, axis.min +
  16941. axis.minRange) :
  16942. threshold;
  16943. }
  16944. }
  16945. // get tickInterval
  16946. if (axis.min === axis.max ||
  16947. typeof axis.min === 'undefined' ||
  16948. typeof axis.max === 'undefined') {
  16949. axis.tickInterval = 1;
  16950. }
  16951. else if (isLinked &&
  16952. !tickIntervalOption &&
  16953. tickPixelIntervalOption ===
  16954. axis.linkedParent.options.tickPixelInterval) {
  16955. axis.tickInterval = tickIntervalOption =
  16956. axis.linkedParent.tickInterval;
  16957. }
  16958. else {
  16959. axis.tickInterval = pick(tickIntervalOption, this.tickAmount ?
  16960. ((axis.max - axis.min) /
  16961. Math.max(this.tickAmount - 1, 1)) :
  16962. void 0,
  16963. // For categoried axis, 1 is default, for linear axis use
  16964. // tickPix
  16965. categories ?
  16966. 1 :
  16967. // don't let it be more than the data range
  16968. (axis.max - axis.min) *
  16969. tickPixelIntervalOption /
  16970. Math.max(axis.len, tickPixelIntervalOption));
  16971. }
  16972. // Now we're finished detecting min and max, crop and group series data.
  16973. // This is in turn needed in order to find tick positions in ordinal
  16974. // axes.
  16975. if (isXAxis && !secondPass) {
  16976. axis.series.forEach(function (series) {
  16977. series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax);
  16978. });
  16979. }
  16980. // set the translation factor used in translate function
  16981. axis.setAxisTranslation(true);
  16982. // hook for ordinal axes and radial axes
  16983. if (axis.beforeSetTickPositions) {
  16984. axis.beforeSetTickPositions();
  16985. }
  16986. // hook for extensions, used in Highstock ordinal axes
  16987. if (axis.postProcessTickInterval) {
  16988. axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval);
  16989. }
  16990. // In column-like charts, don't cramp in more ticks than there are
  16991. // points (#1943, #4184)
  16992. if (axis.pointRange && !tickIntervalOption) {
  16993. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  16994. }
  16995. // Before normalizing the tick interval, handle minimum tick interval.
  16996. // This applies only if tickInterval is not defined.
  16997. minTickInterval = pick(options.minTickInterval, (axis.isDatetimeAxis && axis.closestPointRange));
  16998. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  16999. axis.tickInterval = minTickInterval;
  17000. }
  17001. // for linear axes, get magnitude and normalize the interval
  17002. if (!isDatetimeAxis && !isLog && !tickIntervalOption) {
  17003. axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, getMagnitude(axis.tickInterval),
  17004. // If the tick interval is between 0.5 and 5 and the axis max is
  17005. // in the order of thousands, chances are we are dealing with
  17006. // years. Don't allow decimals. #3363.
  17007. pick(options.allowDecimals, !(axis.tickInterval > 0.5 &&
  17008. axis.tickInterval < 5 &&
  17009. axis.max > 1000 &&
  17010. axis.max < 9999)), !!this.tickAmount);
  17011. }
  17012. // Prevent ticks from getting so close that we can't draw the labels
  17013. if (!this.tickAmount) {
  17014. axis.tickInterval = axis.unsquish();
  17015. }
  17016. this.setTickPositions();
  17017. },
  17018. /**
  17019. * Now we have computed the normalized tickInterval, get the tick positions
  17020. *
  17021. * @function Highcharts.Axis#setTickPositions
  17022. *
  17023. * @fires Highcharts.Axis#event:afterSetTickPositions
  17024. */
  17025. setTickPositions: function () {
  17026. var options = this.options, tickPositions, tickPositionsOption = options.tickPositions, minorTickIntervalOption = this.getMinorTickInterval(), tickPositioner = options.tickPositioner, startOnTick = options.startOnTick, endOnTick = options.endOnTick;
  17027. // Set the tickmarkOffset
  17028. this.tickmarkOffset = (this.categories &&
  17029. options.tickmarkPlacement === 'between' &&
  17030. this.tickInterval === 1) ? 0.5 : 0; // #3202
  17031. // get minorTickInterval
  17032. this.minorTickInterval =
  17033. minorTickIntervalOption === 'auto' &&
  17034. this.tickInterval ?
  17035. this.tickInterval / 5 :
  17036. minorTickIntervalOption;
  17037. // When there is only one point, or all points have the same value on
  17038. // this axis, then min and max are equal and tickPositions.length is 0
  17039. // or 1. In this case, add some padding in order to center the point,
  17040. // but leave it with one tick. #1337.
  17041. this.single =
  17042. this.min === this.max &&
  17043. defined(this.min) &&
  17044. !this.tickAmount &&
  17045. (
  17046. // Data is on integer (#6563)
  17047. parseInt(this.min, 10) === this.min ||
  17048. // Between integers and decimals are not allowed (#6274)
  17049. options.allowDecimals !== false);
  17050. /**
  17051. * Contains the current positions that are laid out on the axis. The
  17052. * positions are numbers in terms of axis values. In a category axis
  17053. * they are integers, in a datetime axis they are also integers, but
  17054. * designating milliseconds.
  17055. *
  17056. * This property is read only - for modifying the tick positions, use
  17057. * the `tickPositioner` callback or [axis.tickPositions(
  17058. * https://api.highcharts.com/highcharts/xAxis.tickPositions) option
  17059. * instead.
  17060. *
  17061. * @name Highcharts.Axis#tickPositions
  17062. * @type {Array<number>|undefined}
  17063. */
  17064. this.tickPositions =
  17065. // Find the tick positions. Work on a copy (#1565)
  17066. tickPositions =
  17067. (tickPositionsOption && tickPositionsOption.slice());
  17068. if (!tickPositions) {
  17069. // Too many ticks (#6405). Create a friendly warning and provide two
  17070. // ticks so at least we can show the data series.
  17071. if (!this.ordinalPositions &&
  17072. ((this.max - this.min) /
  17073. this.tickInterval >
  17074. Math.max(2 * this.len, 200))) {
  17075. tickPositions = [this.min, this.max];
  17076. H.error(19, false, this.chart);
  17077. }
  17078. else if (this.isDatetimeAxis) {
  17079. tickPositions = this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, this.ordinalPositions, this.closestPointRange, true);
  17080. }
  17081. else if (this.isLog) {
  17082. tickPositions = this.getLogTickPositions(this.tickInterval, this.min, this.max);
  17083. }
  17084. else {
  17085. tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
  17086. }
  17087. // Too dense ticks, keep only the first and last (#4477)
  17088. if (tickPositions.length > this.len) {
  17089. tickPositions = [tickPositions[0], tickPositions.pop()];
  17090. // Reduce doubled value (#7339)
  17091. if (tickPositions[0] === tickPositions[1]) {
  17092. tickPositions.length = 1;
  17093. }
  17094. }
  17095. this.tickPositions = tickPositions;
  17096. // Run the tick positioner callback, that allows modifying auto tick
  17097. // positions.
  17098. if (tickPositioner) {
  17099. tickPositioner = tickPositioner.apply(this, [this.min, this.max]);
  17100. if (tickPositioner) {
  17101. this.tickPositions = tickPositions = tickPositioner;
  17102. }
  17103. }
  17104. }
  17105. // Reset min/max or remove extremes based on start/end on tick
  17106. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  17107. this.trimTicks(tickPositions, startOnTick, endOnTick);
  17108. if (!this.isLinked) {
  17109. // Substract half a unit (#2619, #2846, #2515, #3390),
  17110. // but not in case of multiple ticks (#6897)
  17111. if (this.single && tickPositions.length < 2 && !this.categories) {
  17112. this.min -= 0.5;
  17113. this.max += 0.5;
  17114. }
  17115. if (!tickPositionsOption && !tickPositioner) {
  17116. this.adjustTickAmount();
  17117. }
  17118. }
  17119. fireEvent(this, 'afterSetTickPositions');
  17120. },
  17121. /**
  17122. * Handle startOnTick and endOnTick by either adapting to padding min/max or
  17123. * rounded min/max. Also handle single data points.
  17124. * @private
  17125. */
  17126. trimTicks: function (tickPositions, startOnTick, endOnTick) {
  17127. var roundedMin = tickPositions[0], roundedMax = tickPositions[tickPositions.length - 1], minPointOffset = this.minPointOffset || 0;
  17128. fireEvent(this, 'trimTicks');
  17129. if (!this.isLinked) {
  17130. if (startOnTick && roundedMin !== -Infinity) { // #6502
  17131. this.min = roundedMin;
  17132. }
  17133. else {
  17134. while (this.min - minPointOffset > tickPositions[0]) {
  17135. tickPositions.shift();
  17136. }
  17137. }
  17138. if (endOnTick) {
  17139. this.max = roundedMax;
  17140. }
  17141. else {
  17142. while (this.max + minPointOffset <
  17143. tickPositions[tickPositions.length - 1]) {
  17144. tickPositions.pop();
  17145. }
  17146. }
  17147. // If no tick are left, set one tick in the middle (#3195)
  17148. if (tickPositions.length === 0 &&
  17149. defined(roundedMin) &&
  17150. !this.options.tickPositions) {
  17151. tickPositions.push((roundedMax + roundedMin) / 2);
  17152. }
  17153. }
  17154. },
  17155. /**
  17156. * Check if there are multiple axes in the same pane.
  17157. * @private
  17158. * @return {boolean|undefined} True if there are other axes.
  17159. */
  17160. alignToOthers: function () {
  17161. var others = // Whether there is another axis to pair with this one
  17162. {}, hasOther, options = this.options;
  17163. if (
  17164. // Only if alignTicks is true
  17165. this.chart.options.chart.alignTicks !== false &&
  17166. options.alignTicks !== false &&
  17167. // Disabled when startOnTick or endOnTick are false (#7604)
  17168. options.startOnTick !== false &&
  17169. options.endOnTick !== false &&
  17170. // Don't try to align ticks on a log axis, they are not evenly
  17171. // spaced (#6021)
  17172. !this.isLog) {
  17173. this.chart[this.coll].forEach(function (axis) {
  17174. var otherOptions = axis.options, horiz = axis.horiz, key = [
  17175. horiz ? otherOptions.left : otherOptions.top,
  17176. otherOptions.width,
  17177. otherOptions.height,
  17178. otherOptions.pane
  17179. ].join(',');
  17180. if (axis.series.length) { // #4442
  17181. if (others[key]) {
  17182. hasOther = true; // #4201
  17183. }
  17184. else {
  17185. others[key] = 1;
  17186. }
  17187. }
  17188. });
  17189. }
  17190. return hasOther;
  17191. },
  17192. /**
  17193. * Find the max ticks of either the x and y axis collection, and record it
  17194. * in `this.tickAmount`.
  17195. * @private
  17196. * @return {void}
  17197. */
  17198. getTickAmount: function () {
  17199. var options = this.options, tickAmount = options.tickAmount, tickPixelInterval = options.tickPixelInterval;
  17200. if (!defined(options.tickInterval) &&
  17201. this.len < tickPixelInterval &&
  17202. !this.isRadial &&
  17203. !this.isLog &&
  17204. options.startOnTick &&
  17205. options.endOnTick) {
  17206. tickAmount = 2;
  17207. }
  17208. if (!tickAmount && this.alignToOthers()) {
  17209. // Add 1 because 4 tick intervals require 5 ticks (including first
  17210. // and last)
  17211. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  17212. }
  17213. // For tick amounts of 2 and 3, compute five ticks and remove the
  17214. // intermediate ones. This prevents the axis from adding ticks that are
  17215. // too far away from the data extremes.
  17216. if (tickAmount < 4) {
  17217. this.finalTickAmt = tickAmount;
  17218. tickAmount = 5;
  17219. }
  17220. this.tickAmount = tickAmount;
  17221. },
  17222. /**
  17223. * When using multiple axes, adjust the number of ticks to match the highest
  17224. * number of ticks in that group.
  17225. * @private
  17226. * @return {void}
  17227. */
  17228. adjustTickAmount: function () {
  17229. 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;
  17230. if (axis.hasData()) {
  17231. if (currentTickAmount < tickAmount) {
  17232. min = axis.min;
  17233. while (tickPositions.length < tickAmount) {
  17234. // Extend evenly for both sides unless we're on the
  17235. // threshold (#3965)
  17236. if (tickPositions.length % 2 ||
  17237. min === threshold) {
  17238. // to the end
  17239. tickPositions.push(correctFloat(tickPositions[tickPositions.length - 1] +
  17240. tickInterval));
  17241. }
  17242. else {
  17243. // to the start
  17244. tickPositions.unshift(correctFloat(tickPositions[0] - tickInterval));
  17245. }
  17246. }
  17247. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  17248. // Do not crop when ticks are not extremes (#9841)
  17249. axis.min = axisOptions.startOnTick ?
  17250. tickPositions[0] :
  17251. Math.min(axis.min, tickPositions[0]);
  17252. axis.max = axisOptions.endOnTick ?
  17253. tickPositions[tickPositions.length - 1] :
  17254. Math.max(axis.max, tickPositions[tickPositions.length - 1]);
  17255. // We have too many ticks, run second pass to try to reduce ticks
  17256. }
  17257. else if (currentTickAmount > tickAmount) {
  17258. axis.tickInterval *= 2;
  17259. axis.setTickPositions();
  17260. }
  17261. // The finalTickAmt property is set in getTickAmount
  17262. if (defined(finalTickAmt)) {
  17263. i = len = tickPositions.length;
  17264. while (i--) {
  17265. if (
  17266. // Remove every other tick
  17267. (finalTickAmt === 3 && i % 2 === 1) ||
  17268. // Remove all but first and last
  17269. (finalTickAmt <= 2 && i > 0 && i < len - 1)) {
  17270. tickPositions.splice(i, 1);
  17271. }
  17272. }
  17273. axis.finalTickAmt = void 0;
  17274. }
  17275. }
  17276. },
  17277. /**
  17278. * Set the scale based on data min and max, user set min and max or options.
  17279. * @private
  17280. * @return {void}
  17281. * @fires Highcharts.Axis#event:afterSetScale
  17282. */
  17283. setScale: function () {
  17284. var axis = this, isDirtyData = axis.series.some(function (series) {
  17285. return (series.isDirtyData ||
  17286. series.isDirty ||
  17287. // When x axis is dirty, we need new data extremes for y as
  17288. // well:
  17289. series.xAxis && series.xAxis.isDirty);
  17290. }), isDirtyAxisLength;
  17291. axis.oldMin = axis.min;
  17292. axis.oldMax = axis.max;
  17293. axis.oldAxisLength = axis.len;
  17294. // set the new axisLength
  17295. axis.setAxisSize();
  17296. isDirtyAxisLength = axis.len !== axis.oldAxisLength;
  17297. // do we really need to go through all this?
  17298. if (isDirtyAxisLength ||
  17299. isDirtyData ||
  17300. axis.isLinked ||
  17301. axis.forceRedraw ||
  17302. axis.userMin !== axis.oldUserMin ||
  17303. axis.userMax !== axis.oldUserMax ||
  17304. axis.alignToOthers()) {
  17305. if (axis.resetStacks) {
  17306. axis.resetStacks();
  17307. }
  17308. axis.forceRedraw = false;
  17309. // get data extremes if needed
  17310. axis.getSeriesExtremes();
  17311. // get fixed positions based on tickInterval
  17312. axis.setTickInterval();
  17313. // record old values to decide whether a rescale is necessary later
  17314. // on (#540)
  17315. axis.oldUserMin = axis.userMin;
  17316. axis.oldUserMax = axis.userMax;
  17317. // Mark as dirty if it is not already set to dirty and extremes have
  17318. // changed. #595.
  17319. if (!axis.isDirty) {
  17320. axis.isDirty =
  17321. isDirtyAxisLength ||
  17322. axis.min !== axis.oldMin ||
  17323. axis.max !== axis.oldMax;
  17324. }
  17325. }
  17326. else if (axis.cleanStacks) {
  17327. axis.cleanStacks();
  17328. }
  17329. fireEvent(this, 'afterSetScale');
  17330. },
  17331. /**
  17332. * Set the minimum and maximum of the axes after render time. If the
  17333. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  17334. * values are rounded off to the nearest tick. To prevent this, these
  17335. * options can be set to false before calling setExtremes. Also, setExtremes
  17336. * will not allow a range lower than the `minRange` option, which by default
  17337. * is the range of five points.
  17338. *
  17339. * @sample highcharts/members/axis-setextremes/
  17340. * Set extremes from a button
  17341. * @sample highcharts/members/axis-setextremes-datetime/
  17342. * Set extremes on a datetime axis
  17343. * @sample highcharts/members/axis-setextremes-off-ticks/
  17344. * Set extremes off ticks
  17345. * @sample stock/members/axis-setextremes/
  17346. * Set extremes in Highstock
  17347. * @sample maps/members/axis-setextremes/
  17348. * Set extremes in Highmaps
  17349. *
  17350. * @function Highcharts.Axis#setExtremes
  17351. *
  17352. * @param {number} [newMin]
  17353. * The new minimum value.
  17354. *
  17355. * @param {number} [newMax]
  17356. * The new maximum value.
  17357. *
  17358. * @param {boolean} [redraw=true]
  17359. * Whether to redraw the chart or wait for an explicit call to
  17360. * {@link Highcharts.Chart#redraw}
  17361. *
  17362. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  17363. * Enable or modify animations.
  17364. *
  17365. * @param {*} [eventArguments]
  17366. * Arguments to be accessed in event handler.
  17367. *
  17368. * @return {void}
  17369. *
  17370. * @fires Highcharts.Axis#event:setExtremes
  17371. */
  17372. setExtremes: function (newMin, newMax, redraw, animation, eventArguments) {
  17373. var axis = this, chart = axis.chart;
  17374. redraw = pick(redraw, true); // defaults to true
  17375. axis.series.forEach(function (serie) {
  17376. delete serie.kdTree;
  17377. });
  17378. // Extend the arguments with min and max
  17379. eventArguments = extend(eventArguments, {
  17380. min: newMin,
  17381. max: newMax
  17382. });
  17383. // Fire the event
  17384. fireEvent(axis, 'setExtremes', eventArguments, function () {
  17385. axis.userMin = newMin;
  17386. axis.userMax = newMax;
  17387. axis.eventArgs = eventArguments;
  17388. if (redraw) {
  17389. chart.redraw(animation);
  17390. }
  17391. });
  17392. },
  17393. /**
  17394. * Overridable method for zooming chart. Pulled out in a separate method to
  17395. * allow overriding in stock charts.
  17396. * @private
  17397. * @function Highcharts.Axis#zoom
  17398. * @param {number} newMin
  17399. * @param {number} newMax
  17400. * @return {boolean}
  17401. */
  17402. zoom: function (newMin, newMax) {
  17403. var 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 = {
  17404. newMin: newMin,
  17405. newMax: newMax
  17406. };
  17407. fireEvent(this, 'zoom', evt, function (e) {
  17408. // Use e.newMin and e.newMax - event handlers may have altered them
  17409. var newMin = e.newMin, newMax = e.newMax;
  17410. if (newMin !== this.min || newMax !== this.max) { // #5790
  17411. // Prevent pinch zooming out of range. Check for defined is for
  17412. // #1946. #1734.
  17413. if (!this.allowZoomOutside) {
  17414. // #6014, sometimes newMax will be smaller than min (or
  17415. // newMin will be larger than max).
  17416. if (defined(dataMin)) {
  17417. if (newMin < min) {
  17418. newMin = min;
  17419. }
  17420. if (newMin > max) {
  17421. newMin = max;
  17422. }
  17423. }
  17424. if (defined(dataMax)) {
  17425. if (newMax < min) {
  17426. newMax = min;
  17427. }
  17428. if (newMax > max) {
  17429. newMax = max;
  17430. }
  17431. }
  17432. }
  17433. // In full view, displaying the reset zoom button is not
  17434. // required
  17435. this.displayBtn = (typeof newMin !== 'undefined' ||
  17436. typeof newMax !== 'undefined');
  17437. // Do it
  17438. this.setExtremes(newMin, newMax, false, void 0, { trigger: 'zoom' });
  17439. }
  17440. e.zoomed = true;
  17441. });
  17442. return evt.zoomed;
  17443. },
  17444. /**
  17445. * Update the axis metrics.
  17446. * @private
  17447. * @return {void}
  17448. */
  17449. setAxisSize: function () {
  17450. var chart = this.chart, options = this.options,
  17451. // [top, right, bottom, left]
  17452. offsets = options.offsets || [0, 0, 0, 0], horiz = this.horiz,
  17453. // Check for percentage based input values. Rounding fixes problems
  17454. // with column overflow and plot line filtering (#4898, #4899)
  17455. 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));
  17456. // Expose basic values to use in Series object and navigator
  17457. this.bottom = chart.chartHeight - height - top;
  17458. this.right = chart.chartWidth - width - left;
  17459. // Direction agnostic properties
  17460. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  17461. this.pos = horiz ? left : top; // distance from SVG origin
  17462. },
  17463. /**
  17464. * Get the current extremes for the axis.
  17465. *
  17466. * @sample highcharts/members/axis-getextremes/
  17467. * Report extremes by click on a button
  17468. * @sample maps/members/axis-getextremes/
  17469. * Get extremes in Highmaps
  17470. *
  17471. * @function Highcharts.Axis#getExtremes
  17472. *
  17473. * @returns {Highcharts.ExtremesObject}
  17474. * An object containing extremes information.
  17475. */
  17476. getExtremes: function () {
  17477. var axis = this, isLog = axis.isLog;
  17478. return {
  17479. min: isLog ?
  17480. correctFloat(axis.lin2log(axis.min)) :
  17481. axis.min,
  17482. max: isLog ?
  17483. correctFloat(axis.lin2log(axis.max)) :
  17484. axis.max,
  17485. dataMin: axis.dataMin,
  17486. dataMax: axis.dataMax,
  17487. userMin: axis.userMin,
  17488. userMax: axis.userMax
  17489. };
  17490. },
  17491. /**
  17492. * Get the zero plane either based on zero or on the min or max value.
  17493. * Used in bar and area plots.
  17494. *
  17495. * @function Highcharts.Axis#getThreshold
  17496. *
  17497. * @param {number} threshold
  17498. * The threshold in axis values.
  17499. *
  17500. * @return {number|undefined}
  17501. * The translated threshold position in terms of pixels, and
  17502. * corrected to stay within the axis bounds.
  17503. */
  17504. getThreshold: function (threshold) {
  17505. var axis = this, isLog = axis.isLog, realMin = isLog ? axis.lin2log(axis.min) : axis.min, realMax = isLog ? axis.lin2log(axis.max) : axis.max;
  17506. if (threshold === null || threshold === -Infinity) {
  17507. threshold = realMin;
  17508. }
  17509. else if (threshold === Infinity) {
  17510. threshold = realMax;
  17511. }
  17512. else if (realMin > threshold) {
  17513. threshold = realMin;
  17514. }
  17515. else if (realMax < threshold) {
  17516. threshold = realMax;
  17517. }
  17518. return axis.translate(threshold, 0, 1, 0, 1);
  17519. },
  17520. /**
  17521. * Compute auto alignment for the axis label based on which side the axis is
  17522. * on and the given rotation for the label.
  17523. * @private
  17524. * @param {number} rotation The rotation in degrees as set by either the
  17525. * `rotation` or `autoRotation` options.
  17526. * @return {Highcharts.AlignValue} Can be `"center"`, `"left"` or `"right"`.
  17527. */
  17528. autoLabelAlign: function (rotation) {
  17529. var angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360, evt = { align: 'center' };
  17530. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  17531. if (angle > 15 && angle < 165) {
  17532. e.align = 'right';
  17533. }
  17534. else if (angle > 195 && angle < 345) {
  17535. e.align = 'left';
  17536. }
  17537. });
  17538. return evt.align;
  17539. },
  17540. /**
  17541. * Get the tick length and width for the axis based on axis options.
  17542. * @private
  17543. * @param {string} [prefix]
  17544. * 'tick' or 'minorTick'
  17545. * @return {Array<number>}
  17546. * An array of tickLength and tickWidth
  17547. */
  17548. tickSize: function (prefix) {
  17549. var options = this.options, tickLength = options[prefix + 'Length'], tickWidth = pick(options[prefix + 'Width'],
  17550. // Default to 1 on linear and datetime X axes
  17551. prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0), e, tickSize;
  17552. if (tickWidth && tickLength) {
  17553. // Negate the length
  17554. if (options[prefix + 'Position'] === 'inside') {
  17555. tickLength = -tickLength;
  17556. }
  17557. tickSize = [tickLength, tickWidth];
  17558. }
  17559. e = { tickSize: tickSize };
  17560. fireEvent(this, 'afterTickSize', e);
  17561. return e.tickSize;
  17562. },
  17563. /**
  17564. * Return the size of the labels.
  17565. * @private
  17566. * @return {Highcharts.FontMetricsObject}
  17567. */
  17568. labelMetrics: function () {
  17569. var index = this.tickPositions && this.tickPositions[0] || 0;
  17570. return this.chart.renderer.fontMetrics(this.options.labels.style &&
  17571. this.options.labels.style.fontSize, this.ticks[index] && this.ticks[index].label);
  17572. },
  17573. /**
  17574. * Prevent the ticks from getting so close we can't draw the labels. On a
  17575. * horizontal axis, this is handled by rotating the labels, removing ticks
  17576. * and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  17577. * @private
  17578. * @return {number}
  17579. */
  17580. unsquish: function () {
  17581. var labelOptions = this.options.labels, horiz = this.horiz, tickInterval = this.tickInterval, newTickInterval = tickInterval, slotSize = this.len / (((this.categories ? 1 : 0) +
  17582. this.max -
  17583. this.min) /
  17584. tickInterval), rotation, rotationOption = labelOptions.rotation, labelMetrics = this.labelMetrics(), step, bestScore = Number.MAX_VALUE, autoRotation, range = this.max - this.min,
  17585. // Return the multiple of tickInterval that is needed to avoid
  17586. // collision
  17587. getStep = function (spaceNeeded) {
  17588. var step = spaceNeeded / (slotSize || 1);
  17589. step = step > 1 ? Math.ceil(step) : 1;
  17590. // Guard for very small or negative angles (#9835)
  17591. if (step * tickInterval > range &&
  17592. spaceNeeded !== Infinity &&
  17593. slotSize !== Infinity &&
  17594. range) {
  17595. step = Math.ceil(range / tickInterval);
  17596. }
  17597. return correctFloat(step * tickInterval);
  17598. };
  17599. if (horiz) {
  17600. autoRotation = !labelOptions.staggerLines &&
  17601. !labelOptions.step &&
  17602. ( // #3971
  17603. defined(rotationOption) ?
  17604. [rotationOption] :
  17605. slotSize < pick(labelOptions.autoRotationLimit, 80) && labelOptions.autoRotation);
  17606. if (autoRotation) {
  17607. // Loop over the given autoRotation options, and determine
  17608. // which gives the best score. The best score is that with
  17609. // the lowest number of steps and a rotation closest
  17610. // to horizontal.
  17611. autoRotation.forEach(function (rot) {
  17612. var score;
  17613. if (rot === rotationOption ||
  17614. (rot && rot >= -90 && rot <= 90)) { // #3891
  17615. step = getStep(Math.abs(labelMetrics.h / Math.sin(deg2rad * rot)));
  17616. score = step + Math.abs(rot / 360);
  17617. if (score < bestScore) {
  17618. bestScore = score;
  17619. rotation = rot;
  17620. newTickInterval = step;
  17621. }
  17622. }
  17623. });
  17624. }
  17625. }
  17626. else if (!labelOptions.step) { // #4411
  17627. newTickInterval = getStep(labelMetrics.h);
  17628. }
  17629. this.autoRotation = autoRotation;
  17630. this.labelRotation = pick(rotation, rotationOption);
  17631. return newTickInterval;
  17632. },
  17633. /**
  17634. * Get the general slot width for labels/categories on this axis. This may
  17635. * change between the pre-render (from Axis.getOffset) and the final tick
  17636. * rendering and placement.
  17637. * @private
  17638. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  17639. * basing on tick label. It is used in highcharts-3d module, where the slots
  17640. * has different widths depending on perspective angles.
  17641. * @return {number} The pixel width allocated to each axis label.
  17642. */
  17643. getSlotWidth: function (tick) {
  17644. // #5086, #1580, #1931
  17645. 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];
  17646. return (tick &&
  17647. tick.slotWidth // Used by grid axis
  17648. ) || (horiz &&
  17649. (labelOptions.step || 0) < 2 &&
  17650. !labelOptions.rotation && // #4415
  17651. ((this.staggerLines || 1) * this.len) / slotCount) || (!horiz && (
  17652. // #7028
  17653. (labelOptions.style &&
  17654. parseInt(labelOptions.style.width, 10)) ||
  17655. (marginLeft &&
  17656. (marginLeft - chart.spacing[3])) ||
  17657. chart.chartWidth * 0.33));
  17658. },
  17659. /**
  17660. * Render the axis labels and determine whether ellipsis or rotation need to
  17661. * be applied.
  17662. * @private
  17663. * @return {void}
  17664. */
  17665. renderUnsquish: function () {
  17666. 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 &&
  17667. labelOptions.style.textOverflow), commonWidth, commonTextOverflow, maxLabelLength = 0, label, i, pos;
  17668. // Set rotation option unless it is "auto", like in gauges
  17669. if (!isString(labelOptions.rotation)) {
  17670. // #4443:
  17671. attr.rotation = labelOptions.rotation || 0;
  17672. }
  17673. // Get the longest label length
  17674. tickPositions.forEach(function (tick) {
  17675. tick = ticks[tick];
  17676. // Replace label - sorting animation
  17677. if (tick.movedLabel) {
  17678. tick.replaceMovedLabel();
  17679. }
  17680. if (tick &&
  17681. tick.label &&
  17682. tick.label.textPxLength > maxLabelLength) {
  17683. maxLabelLength = tick.label.textPxLength;
  17684. }
  17685. });
  17686. this.maxLabelLength = maxLabelLength;
  17687. // Handle auto rotation on horizontal axis
  17688. if (this.autoRotation) {
  17689. // Apply rotation only if the label is too wide for the slot, and
  17690. // the label is wider than its height.
  17691. if (maxLabelLength > innerWidth &&
  17692. maxLabelLength > labelMetrics.h) {
  17693. attr.rotation = this.labelRotation;
  17694. }
  17695. else {
  17696. this.labelRotation = 0;
  17697. }
  17698. // Handle word-wrap or ellipsis on vertical axis
  17699. }
  17700. else if (slotWidth) {
  17701. // For word-wrap or ellipsis
  17702. commonWidth = innerWidth;
  17703. if (!textOverflowOption) {
  17704. commonTextOverflow = 'clip';
  17705. // On vertical axis, only allow word wrap if there is room
  17706. // for more lines.
  17707. i = tickPositions.length;
  17708. while (!horiz && i--) {
  17709. pos = tickPositions[i];
  17710. label = ticks[pos].label;
  17711. if (label) {
  17712. // Reset ellipsis in order to get the correct
  17713. // bounding box (#4070)
  17714. if (label.styles &&
  17715. label.styles.textOverflow === 'ellipsis') {
  17716. label.css({ textOverflow: 'clip' });
  17717. // Set the correct width in order to read
  17718. // the bounding box height (#4678, #5034)
  17719. }
  17720. else if (label.textPxLength > slotWidth) {
  17721. label.css({ width: slotWidth + 'px' });
  17722. }
  17723. if (label.getBBox().height > (this.len / tickPositions.length -
  17724. (labelMetrics.h - labelMetrics.f))) {
  17725. label.specificTextOverflow = 'ellipsis';
  17726. }
  17727. }
  17728. }
  17729. }
  17730. }
  17731. // Add ellipsis if the label length is significantly longer than ideal
  17732. if (attr.rotation) {
  17733. commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
  17734. chart.chartHeight * 0.33 :
  17735. maxLabelLength);
  17736. if (!textOverflowOption) {
  17737. commonTextOverflow = 'ellipsis';
  17738. }
  17739. }
  17740. // Set the explicit or automatic label alignment
  17741. this.labelAlign = labelOptions.align ||
  17742. this.autoLabelAlign(this.labelRotation);
  17743. if (this.labelAlign) {
  17744. attr.align = this.labelAlign;
  17745. }
  17746. // Apply general and specific CSS
  17747. tickPositions.forEach(function (pos) {
  17748. var tick = ticks[pos], label = tick && tick.label, widthOption = labelStyleOptions.width, css = {};
  17749. if (label) {
  17750. // This needs to go before the CSS in old IE (#4502)
  17751. label.attr(attr);
  17752. if (tick.shortenLabel) {
  17753. tick.shortenLabel();
  17754. }
  17755. else if (commonWidth &&
  17756. !widthOption &&
  17757. // Setting width in this case messes with the bounding box
  17758. // (#7975)
  17759. labelStyleOptions.whiteSpace !== 'nowrap' &&
  17760. (
  17761. // Speed optimizing, #7656
  17762. commonWidth < label.textPxLength ||
  17763. // Resetting CSS, #4928
  17764. label.element.tagName === 'SPAN')) {
  17765. css.width = commonWidth;
  17766. if (!textOverflowOption) {
  17767. css.textOverflow = (label.specificTextOverflow ||
  17768. commonTextOverflow);
  17769. }
  17770. label.css(css);
  17771. // Reset previously shortened label (#8210)
  17772. }
  17773. else if (label.styles &&
  17774. label.styles.width &&
  17775. !css.width &&
  17776. !widthOption) {
  17777. label.css({ width: null });
  17778. }
  17779. delete label.specificTextOverflow;
  17780. tick.rotation = attr.rotation;
  17781. }
  17782. }, this);
  17783. // Note: Why is this not part of getLabelPosition?
  17784. this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
  17785. },
  17786. /**
  17787. * Return true if the axis has associated data.
  17788. *
  17789. * @function Highcharts.Axis#hasData
  17790. *
  17791. * @return {boolean}
  17792. * True if the axis has associated visible series and those series
  17793. * have either valid data points or explicit `min` and `max`
  17794. * settings.
  17795. */
  17796. hasData: function () {
  17797. return this.series.some(function (s) {
  17798. return s.hasData();
  17799. }) ||
  17800. (this.options.showEmpty &&
  17801. defined(this.min) &&
  17802. defined(this.max));
  17803. },
  17804. /**
  17805. * Adds the title defined in axis.options.title.
  17806. *
  17807. * @function Highcharts.Axis#addTitle
  17808. *
  17809. * @param {boolean} [display]
  17810. * Whether or not to display the title.
  17811. *
  17812. * @return {void}
  17813. */
  17814. addTitle: function (display) {
  17815. var axis = this, renderer = axis.chart.renderer, horiz = axis.horiz, opposite = axis.opposite, options = axis.options, axisTitleOptions = options.title, textAlign, styledMode = axis.chart.styledMode;
  17816. if (!axis.axisTitle) {
  17817. textAlign = axisTitleOptions.textAlign;
  17818. if (!textAlign) {
  17819. textAlign = (horiz ? {
  17820. low: 'left',
  17821. middle: 'center',
  17822. high: 'right'
  17823. } : {
  17824. low: opposite ? 'right' : 'left',
  17825. middle: 'center',
  17826. high: opposite ? 'left' : 'right'
  17827. })[axisTitleOptions.align];
  17828. }
  17829. axis.axisTitle = renderer
  17830. .text(axisTitleOptions.text, 0, 0, axisTitleOptions.useHTML)
  17831. .attr({
  17832. zIndex: 7,
  17833. rotation: axisTitleOptions.rotation || 0,
  17834. align: textAlign
  17835. })
  17836. .addClass('highcharts-axis-title');
  17837. // #7814, don't mutate style option
  17838. if (!styledMode) {
  17839. axis.axisTitle.css(merge(axisTitleOptions.style));
  17840. }
  17841. axis.axisTitle.add(axis.axisGroup);
  17842. axis.axisTitle.isNew = true;
  17843. }
  17844. // Max width defaults to the length of the axis
  17845. if (!styledMode &&
  17846. !axisTitleOptions.style.width &&
  17847. !axis.isRadial) {
  17848. axis.axisTitle.css({
  17849. width: axis.len
  17850. });
  17851. }
  17852. // hide or show the title depending on whether showEmpty is set
  17853. axis.axisTitle[display ? 'show' : 'hide'](display);
  17854. },
  17855. /**
  17856. * Generates a tick for initial positioning.
  17857. * @private
  17858. * @param {number} pos The tick position in axis values.
  17859. * @param {number} [i] The index of the tick in {@link Axis.tickPositions}.
  17860. */
  17861. generateTick: function (pos) {
  17862. var ticks = this.ticks;
  17863. if (!ticks[pos]) {
  17864. ticks[pos] = new Tick(this, pos);
  17865. }
  17866. else {
  17867. ticks[pos].addLabel(); // update labels depending on tick interval
  17868. }
  17869. },
  17870. /**
  17871. * Render the tick labels to a preliminary position to get their sizes
  17872. * @private
  17873. * @return {void}
  17874. * @fires Highcharts.Axis#event:afterGetOffset
  17875. */
  17876. getOffset: function () {
  17877. 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 &&
  17878. !axis.isZAxis ? [1, 0, 3, 2][side] : side, hasData, showAxis, titleOffset = 0, titleOffsetOption, titleMargin = 0, axisTitleOptions = options.title, labelOptions = options.labels, labelOffset = 0, // reset
  17879. labelOffsetPadded, axisOffset = chart.axisOffset, clipOffset = chart.clipOffset, clip, directionFactor = [-1, 1, 1, -1][side], className = options.className, axisParent = axis.axisParent, // Used in color axis
  17880. lineHeightCorrection, tickSize;
  17881. // For reuse in Axis.render
  17882. hasData = axis.hasData();
  17883. axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
  17884. // Set/reset staggerLines
  17885. axis.staggerLines = axis.horiz && labelOptions.staggerLines;
  17886. // Create the axisGroup and gridGroup elements on first iteration
  17887. if (!axis.axisGroup) {
  17888. axis.gridGroup = renderer.g('grid')
  17889. .attr({ zIndex: options.gridZIndex || 1 })
  17890. .addClass('highcharts-' + this.coll.toLowerCase() + '-grid ' +
  17891. (className || ''))
  17892. .add(axisParent);
  17893. axis.axisGroup = renderer.g('axis')
  17894. .attr({ zIndex: options.zIndex || 2 })
  17895. .addClass('highcharts-' + this.coll.toLowerCase() + ' ' +
  17896. (className || ''))
  17897. .add(axisParent);
  17898. axis.labelGroup = renderer.g('axis-labels')
  17899. .attr({ zIndex: labelOptions.zIndex || 7 })
  17900. .addClass('highcharts-' + axis.coll.toLowerCase() + '-labels ' +
  17901. (className || ''))
  17902. .add(axisParent);
  17903. }
  17904. if (hasData || axis.isLinked) {
  17905. // Generate ticks
  17906. tickPositions.forEach(function (pos, i) {
  17907. // i is not used here, but may be used in overrides
  17908. axis.generateTick(pos, i);
  17909. });
  17910. axis.renderUnsquish();
  17911. // Left side must be align: right and right side must
  17912. // have align: left for labels
  17913. axis.reserveSpaceDefault = (side === 0 ||
  17914. side === 2 ||
  17915. { 1: 'left', 3: 'right' }[side] === axis.labelAlign);
  17916. if (pick(labelOptions.reserveSpace, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
  17917. tickPositions.forEach(function (pos) {
  17918. // get the highest offset
  17919. labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
  17920. });
  17921. }
  17922. if (axis.staggerLines) {
  17923. labelOffset *= axis.staggerLines;
  17924. }
  17925. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  17926. }
  17927. else { // doesn't have data
  17928. objectEach(ticks, function (tick, n) {
  17929. tick.destroy();
  17930. delete ticks[n];
  17931. });
  17932. }
  17933. if (axisTitleOptions &&
  17934. axisTitleOptions.text &&
  17935. axisTitleOptions.enabled !== false) {
  17936. axis.addTitle(showAxis);
  17937. if (showAxis && axisTitleOptions.reserveSpace !== false) {
  17938. axis.titleOffset = titleOffset =
  17939. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  17940. titleOffsetOption = axisTitleOptions.offset;
  17941. titleMargin = defined(titleOffsetOption) ?
  17942. 0 :
  17943. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  17944. }
  17945. }
  17946. // Render the axis line
  17947. axis.renderLine();
  17948. // handle automatic or user set offset
  17949. axis.offset = directionFactor * pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
  17950. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  17951. if (side === 0) {
  17952. lineHeightCorrection = -axis.labelMetrics().h;
  17953. }
  17954. else if (side === 2) {
  17955. lineHeightCorrection = axis.tickRotCorr.y;
  17956. }
  17957. else {
  17958. lineHeightCorrection = 0;
  17959. }
  17960. // Find the padded label offset
  17961. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  17962. if (labelOffset) {
  17963. labelOffsetPadded -= lineHeightCorrection;
  17964. labelOffsetPadded += directionFactor * (horiz ?
  17965. pick(labelOptions.y, axis.tickRotCorr.y + directionFactor * 8) :
  17966. labelOptions.x);
  17967. }
  17968. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  17969. if (axis.getMaxLabelDimensions) {
  17970. axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
  17971. }
  17972. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  17973. // has rendered.
  17974. tickSize = this.tickSize('tick');
  17975. axisOffset[side] = Math.max(axisOffset[side], axis.axisTitleMargin + titleOffset +
  17976. directionFactor * axis.offset, labelOffsetPadded, // #3027
  17977. tickPositions && tickPositions.length && tickSize ?
  17978. tickSize[0] + directionFactor * axis.offset :
  17979. 0 // #4866
  17980. );
  17981. // Decide the clipping needed to keep the graph inside
  17982. // the plot area and axis lines
  17983. clip = options.offset ?
  17984. 0 :
  17985. // #4308, #4371:
  17986. Math.floor(axis.axisLine.strokeWidth() / 2) * 2;
  17987. clipOffset[invertedSide] =
  17988. Math.max(clipOffset[invertedSide], clip);
  17989. fireEvent(this, 'afterGetOffset');
  17990. },
  17991. /**
  17992. * Internal function to get the path for the axis line. Extended for polar
  17993. * charts.
  17994. *
  17995. * @function Highcharts.Axis#getLinePath
  17996. *
  17997. * @param {number} lineWidth
  17998. * The line width in pixels.
  17999. *
  18000. * @return {Highcharts.SVGPathArray}
  18001. * The SVG path definition in array form.
  18002. */
  18003. getLinePath: function (lineWidth) {
  18004. 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 -
  18005. (opposite ? this.height : 0) + offset;
  18006. if (opposite) {
  18007. lineWidth *= -1; // crispify the other way - #1480, #1687
  18008. }
  18009. return chart.renderer
  18010. .crispLine([
  18011. 'M',
  18012. horiz ?
  18013. this.left :
  18014. lineLeft,
  18015. horiz ?
  18016. lineTop :
  18017. this.top,
  18018. 'L',
  18019. horiz ?
  18020. chart.chartWidth - this.right :
  18021. lineLeft,
  18022. horiz ?
  18023. lineTop :
  18024. chart.chartHeight - this.bottom
  18025. ], lineWidth);
  18026. },
  18027. /**
  18028. * Render the axis line. Called internally when rendering and redrawing the
  18029. * axis.
  18030. *
  18031. * @function Highcharts.Axis#renderLine
  18032. *
  18033. * @return {void}
  18034. */
  18035. renderLine: function () {
  18036. if (!this.axisLine) {
  18037. this.axisLine = this.chart.renderer.path()
  18038. .addClass('highcharts-axis-line')
  18039. .add(this.axisGroup);
  18040. if (!this.chart.styledMode) {
  18041. this.axisLine.attr({
  18042. stroke: this.options.lineColor,
  18043. 'stroke-width': this.options.lineWidth,
  18044. zIndex: 7
  18045. });
  18046. }
  18047. }
  18048. },
  18049. /**
  18050. * Position the axis title.
  18051. * @private
  18052. * @return {Highcharts.PositionObject} X and Y positions for the title.
  18053. */
  18054. getTitlePosition: function () {
  18055. // compute anchor points for each of the title align options
  18056. 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 &&
  18057. axisTitleOptions.style.fontSize, axisTitle),
  18058. // The part of a multiline text that is below the baseline of the
  18059. // first line. Subtract 1 to preserve pixel-perfectness from the
  18060. // old behaviour (v5.0.12), where only one line was allowed.
  18061. textHeightOvershoot = Math.max(axisTitle.getBBox(null, 0).height - fontMetrics.h - 1, 0),
  18062. // the position in the length direction of the axis
  18063. alongAxis = {
  18064. low: margin + (horiz ? 0 : axisLength),
  18065. middle: margin + axisLength / 2,
  18066. high: margin + (horiz ? axisLength : 0)
  18067. }[axisTitleOptions.align],
  18068. // the position in the perpendicular direction of the axis
  18069. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  18070. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  18071. (opposite ? -1 : 1) * // so does opposite axes
  18072. this.axisTitleMargin +
  18073. [
  18074. -textHeightOvershoot,
  18075. textHeightOvershoot,
  18076. fontMetrics.f,
  18077. -textHeightOvershoot // left
  18078. ][this.side], titlePosition = {
  18079. x: horiz ?
  18080. alongAxis + xOption :
  18081. offAxis + (opposite ? this.width : 0) + offset + xOption,
  18082. y: horiz ?
  18083. offAxis + yOption - (opposite ? this.height : 0) + offset :
  18084. alongAxis + yOption
  18085. };
  18086. fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
  18087. return titlePosition;
  18088. },
  18089. /**
  18090. * Render a minor tick into the given position. If a minor tick already
  18091. * exists in this position, move it.
  18092. *
  18093. * @function Highcharts.Axis#renderMinorTick
  18094. *
  18095. * @param {number} pos
  18096. * The position in axis values.
  18097. *
  18098. * @return {void}
  18099. */
  18100. renderMinorTick: function (pos) {
  18101. var slideInTicks = this.chart.hasRendered && isNumber(this.oldMin), minorTicks = this.minorTicks;
  18102. if (!minorTicks[pos]) {
  18103. minorTicks[pos] = new Tick(this, pos, 'minor');
  18104. }
  18105. // Render new ticks in old position
  18106. if (slideInTicks && minorTicks[pos].isNew) {
  18107. minorTicks[pos].render(null, true);
  18108. }
  18109. minorTicks[pos].render(null, false, 1);
  18110. },
  18111. /**
  18112. * Render a major tick into the given position. If a tick already exists
  18113. * in this position, move it.
  18114. *
  18115. * @function Highcharts.Axis#renderTick
  18116. *
  18117. * @param {number} pos
  18118. * The position in axis values.
  18119. *
  18120. * @param {number} i
  18121. * The tick index.
  18122. *
  18123. * @return {void}
  18124. */
  18125. renderTick: function (pos, i) {
  18126. var isLinked = this.isLinked, ticks = this.ticks, slideInTicks = this.chart.hasRendered && isNumber(this.oldMin);
  18127. // Linked axes need an extra check to find out if
  18128. if (!isLinked ||
  18129. (pos >= this.min && pos <= this.max)) {
  18130. if (!ticks[pos]) {
  18131. ticks[pos] = new Tick(this, pos);
  18132. }
  18133. // NOTE this seems like overkill. Could be handled in tick.render by
  18134. // setting old position in attr, then set new position in animate.
  18135. // render new ticks in old position
  18136. if (slideInTicks && ticks[pos].isNew) {
  18137. // Start with negative opacity so that it is visible from
  18138. // halfway into the animation
  18139. ticks[pos].render(i, true, -1);
  18140. }
  18141. ticks[pos].render(i);
  18142. }
  18143. },
  18144. /**
  18145. * Render the axis.
  18146. * @private
  18147. * @return {void}
  18148. * @fires Highcharts.Axis#event:afterRender
  18149. */
  18150. render: function () {
  18151. var axis = this, chart = axis.chart, renderer = chart.renderer, options = axis.options, isLog = axis.isLog, 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;
  18152. // Reset
  18153. axis.labelEdge.length = 0;
  18154. axis.overlap = false;
  18155. // Mark all elements inActive before we go over and mark the active ones
  18156. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  18157. objectEach(coll, function (tick) {
  18158. tick.isActive = false;
  18159. });
  18160. });
  18161. // If the series has data draw the ticks. Else only the line and title
  18162. if (axis.hasData() || isLinked) {
  18163. // minor ticks
  18164. if (axis.minorTickInterval && !axis.categories) {
  18165. axis.getMinorTickPositions().forEach(function (pos) {
  18166. axis.renderMinorTick(pos);
  18167. });
  18168. }
  18169. // Major ticks. Pull out the first item and render it last so that
  18170. // we can get the position of the neighbour label. #808.
  18171. if (tickPositions.length) { // #1300
  18172. tickPositions.forEach(function (pos, i) {
  18173. axis.renderTick(pos, i);
  18174. });
  18175. // In a categorized axis, the tick marks are displayed
  18176. // between labels. So we need to add a tick mark and
  18177. // grid line at the left edge of the X axis.
  18178. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  18179. if (!ticks[-1]) {
  18180. ticks[-1] = new Tick(axis, -1, null, true);
  18181. }
  18182. ticks[-1].render(-1);
  18183. }
  18184. }
  18185. // alternate grid color
  18186. if (alternateGridColor) {
  18187. tickPositions.forEach(function (pos, i) {
  18188. to = typeof tickPositions[i + 1] !== 'undefined' ?
  18189. tickPositions[i + 1] + tickmarkOffset :
  18190. axis.max - tickmarkOffset;
  18191. if (i % 2 === 0 &&
  18192. pos < axis.max &&
  18193. to <= axis.max + (chart.polar ?
  18194. -tickmarkOffset :
  18195. tickmarkOffset)) { // #2248, #4660
  18196. if (!alternateBands[pos]) {
  18197. alternateBands[pos] = new H.PlotLineOrBand(axis);
  18198. }
  18199. from = pos + tickmarkOffset; // #949
  18200. alternateBands[pos].options = {
  18201. from: isLog ? axis.lin2log(from) : from,
  18202. to: isLog ? axis.lin2log(to) : to,
  18203. color: alternateGridColor
  18204. };
  18205. alternateBands[pos].render();
  18206. alternateBands[pos].isActive = true;
  18207. }
  18208. });
  18209. }
  18210. // custom plot lines and bands
  18211. if (!axis._addedPlotLB) { // only first time
  18212. (options.plotLines || [])
  18213. .concat(options.plotBands || [])
  18214. .forEach(function (plotLineOptions) {
  18215. axis.addPlotBandOrLine(plotLineOptions);
  18216. });
  18217. axis._addedPlotLB = true;
  18218. }
  18219. } // end if hasData
  18220. // Remove inactive ticks
  18221. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  18222. var i, forDestruction = [], delay = animation.duration, destroyInactiveItems = function () {
  18223. i = forDestruction.length;
  18224. while (i--) {
  18225. // When resizing rapidly, the same items
  18226. // may be destroyed in different timeouts,
  18227. // or the may be reactivated
  18228. if (coll[forDestruction[i]] &&
  18229. !coll[forDestruction[i]].isActive) {
  18230. coll[forDestruction[i]].destroy();
  18231. delete coll[forDestruction[i]];
  18232. }
  18233. }
  18234. };
  18235. objectEach(coll, function (tick, pos) {
  18236. if (!tick.isActive) {
  18237. // Render to zero opacity
  18238. tick.render(pos, false, 0);
  18239. tick.isActive = false;
  18240. forDestruction.push(pos);
  18241. }
  18242. });
  18243. // When the objects are finished fading out, destroy them
  18244. syncTimeout(destroyInactiveItems, coll === alternateBands ||
  18245. !chart.hasRendered ||
  18246. !delay ?
  18247. 0 :
  18248. delay);
  18249. });
  18250. // Set the axis line path
  18251. if (axisLine) {
  18252. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  18253. d: this.getLinePath(axisLine.strokeWidth())
  18254. });
  18255. axisLine.isPlaced = true;
  18256. // Show or hide the line depending on options.showEmpty
  18257. axisLine[showAxis ? 'show' : 'hide'](showAxis);
  18258. }
  18259. if (axisTitle && showAxis) {
  18260. var titleXy = axis.getTitlePosition();
  18261. if (isNumber(titleXy.y)) {
  18262. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
  18263. axisTitle.isNew = false;
  18264. }
  18265. else {
  18266. axisTitle.attr('y', -9999);
  18267. axisTitle.isNew = true;
  18268. }
  18269. }
  18270. // Stacked totals:
  18271. if (stackLabelOptions && stackLabelOptions.enabled) {
  18272. axis.renderStackTotals();
  18273. }
  18274. // End stacked totals
  18275. axis.isDirty = false;
  18276. fireEvent(this, 'afterRender');
  18277. },
  18278. /**
  18279. * Redraw the axis to reflect changes in the data or axis extremes. Called
  18280. * internally from Highcharts.Chart#redraw.
  18281. * @private
  18282. * @return {void}
  18283. */
  18284. redraw: function () {
  18285. if (this.visible) {
  18286. // render the axis
  18287. this.render();
  18288. // move plot lines and bands
  18289. this.plotLinesAndBands.forEach(function (plotLine) {
  18290. plotLine.render();
  18291. });
  18292. }
  18293. // mark associated series as dirty and ready for redraw
  18294. this.series.forEach(function (series) {
  18295. series.isDirty = true;
  18296. });
  18297. },
  18298. // Properties to survive after destroy, needed for Axis.update (#4317,
  18299. // #5773, #5881).
  18300. keepProps: ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'],
  18301. /**
  18302. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  18303. * to fully remove the axis.
  18304. * @private
  18305. * @param {boolean} [keepEvents] Whether to preserve events, used internally
  18306. * in Axis.update.
  18307. * @return {void}
  18308. */
  18309. destroy: function (keepEvents) {
  18310. var axis = this, stacks = axis.stacks, plotLinesAndBands = axis.plotLinesAndBands, plotGroup, i;
  18311. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  18312. // Remove the events
  18313. if (!keepEvents) {
  18314. removeEvent(axis);
  18315. }
  18316. // Destroy each stack total
  18317. objectEach(stacks, function (stack, stackKey) {
  18318. destroyObjectProperties(stack);
  18319. stacks[stackKey] = null;
  18320. });
  18321. // Destroy collections
  18322. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
  18323. destroyObjectProperties(coll);
  18324. });
  18325. if (plotLinesAndBands) {
  18326. i = plotLinesAndBands.length;
  18327. while (i--) { // #1975
  18328. plotLinesAndBands[i].destroy();
  18329. }
  18330. }
  18331. // Destroy elements
  18332. ['stackTotalGroup', 'axisLine', 'axisTitle', 'axisGroup',
  18333. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(function (prop) {
  18334. if (axis[prop]) {
  18335. axis[prop] = axis[prop].destroy();
  18336. }
  18337. });
  18338. // Destroy each generated group for plotlines and plotbands
  18339. for (plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
  18340. axis.plotLinesAndBandsGroups[plotGroup] =
  18341. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  18342. }
  18343. // Delete all properties and fall back to the prototype.
  18344. objectEach(axis, function (val, key) {
  18345. if (axis.keepProps.indexOf(key) === -1) {
  18346. delete axis[key];
  18347. }
  18348. });
  18349. },
  18350. /**
  18351. * Internal function to draw a crosshair.
  18352. *
  18353. * @function Highcharts.Axis#drawCrosshair
  18354. *
  18355. * @param {Highcharts.PointerEventObject} [e]
  18356. * The event arguments from the modified pointer event, extended with
  18357. * `chartX` and `chartY`
  18358. *
  18359. * @param {Highcharts.Point} [point]
  18360. * The Point object if the crosshair snaps to points.
  18361. *
  18362. * @return {void}
  18363. *
  18364. * @fires Highcharts.Axis#event:afterDrawCrosshair
  18365. * @fires Highcharts.Axis#event:drawCrosshair
  18366. */
  18367. drawCrosshair: function (e, point) {
  18368. var path, options = this.crosshair, snap = pick(options.snap, true), pos, categorized, graphic = this.cross, crossOptions;
  18369. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  18370. // Use last available event when updating non-snapped crosshairs without
  18371. // mouse interaction (#5287)
  18372. if (!e) {
  18373. e = this.cross && this.cross.e;
  18374. }
  18375. if (
  18376. // Disabled in options
  18377. !this.crosshair ||
  18378. // Snap
  18379. ((defined(point) || !snap) === false)) {
  18380. this.hideCrosshair();
  18381. }
  18382. else {
  18383. // Get the path
  18384. if (!snap) {
  18385. pos = e &&
  18386. (this.horiz ?
  18387. e.chartX - this.pos :
  18388. this.len - e.chartY + this.pos);
  18389. }
  18390. else if (defined(point)) {
  18391. // #3834
  18392. pos = pick(this.coll !== 'colorAxis' ?
  18393. point.crosshairPos : // 3D axis extension
  18394. null, this.isXAxis ?
  18395. point.plotX :
  18396. this.len - point.plotY);
  18397. }
  18398. if (defined(pos)) {
  18399. crossOptions = {
  18400. // value, only used on radial
  18401. value: point && (this.isXAxis ?
  18402. point.x :
  18403. pick(point.stackY, point.y)),
  18404. translatedValue: pos
  18405. };
  18406. if (this.chart.polar) {
  18407. // Additional information required for crosshairs in
  18408. // polar chart
  18409. extend(crossOptions, {
  18410. isCrosshair: true,
  18411. chartX: e && e.chartX,
  18412. chartY: e && e.chartY,
  18413. point: point
  18414. });
  18415. }
  18416. path = this.getPlotLinePath(crossOptions) ||
  18417. null; // #3189
  18418. }
  18419. if (!defined(path)) {
  18420. this.hideCrosshair();
  18421. return;
  18422. }
  18423. categorized = this.categories && !this.isRadial;
  18424. // Draw the cross
  18425. if (!graphic) {
  18426. this.cross = graphic = this.chart.renderer
  18427. .path()
  18428. .addClass('highcharts-crosshair highcharts-crosshair-' +
  18429. (categorized ? 'category ' : 'thin ') +
  18430. options.className)
  18431. .attr({
  18432. zIndex: pick(options.zIndex, 2)
  18433. })
  18434. .add();
  18435. // Presentational attributes
  18436. if (!this.chart.styledMode) {
  18437. graphic.attr({
  18438. stroke: options.color ||
  18439. (categorized ?
  18440. color('#ccd6eb')
  18441. .setOpacity(0.25).get() :
  18442. '#cccccc'),
  18443. 'stroke-width': pick(options.width, 1)
  18444. }).css({
  18445. 'pointer-events': 'none'
  18446. });
  18447. if (options.dashStyle) {
  18448. graphic.attr({
  18449. dashstyle: options.dashStyle
  18450. });
  18451. }
  18452. }
  18453. }
  18454. graphic.show().attr({
  18455. d: path
  18456. });
  18457. if (categorized && !options.width) {
  18458. graphic.attr({
  18459. 'stroke-width': this.transA
  18460. });
  18461. }
  18462. this.cross.e = e;
  18463. }
  18464. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  18465. },
  18466. /**
  18467. * Hide the crosshair if visible.
  18468. *
  18469. * @function Highcharts.Axis#hideCrosshair
  18470. *
  18471. * @return {void}
  18472. */
  18473. hideCrosshair: function () {
  18474. if (this.cross) {
  18475. this.cross.hide();
  18476. }
  18477. fireEvent(this, 'afterHideCrosshair');
  18478. }
  18479. }); // end Axis
  18480. H.Axis = Axis;
  18481. return Axis;
  18482. });
  18483. _registerModule(_modules, 'parts/DateTimeAxis.js', [_modules['parts/Globals.js']], function (H) {
  18484. /* *
  18485. *
  18486. * (c) 2010-2019 Torstein Honsi
  18487. *
  18488. * License: www.highcharts.com/license
  18489. *
  18490. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  18491. *
  18492. * */
  18493. var Axis = H.Axis, getMagnitude = H.getMagnitude, normalizeTickInterval = H.normalizeTickInterval, timeUnits = H.timeUnits;
  18494. /* eslint-disable valid-jsdoc */
  18495. /**
  18496. * Set the tick positions to a time unit that makes sense, for example
  18497. * on the first of each month or on every Monday. Return an array
  18498. * with the time positions. Used in datetime axes as well as for grouping
  18499. * data on a datetime axis.
  18500. *
  18501. * @private
  18502. * @function Highcharts.Axis#getTimeTicks
  18503. *
  18504. * @param {Highcharts.DateTimeAxisNormalizedObject} normalizedInterval
  18505. * The interval in axis values (ms) and thecount
  18506. *
  18507. * @param {number} min
  18508. * The minimum in axis values
  18509. *
  18510. * @param {number} max
  18511. * The maximum in axis values
  18512. *
  18513. * @param {number} startOfWeek
  18514. *
  18515. * @return {Highcharts.AxisTickPositionsArray}
  18516. */
  18517. Axis.prototype.getTimeTicks = function () {
  18518. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  18519. };
  18520. /**
  18521. * Get a normalized tick interval for dates. Returns a configuration object with
  18522. * unit range (interval), count and name. Used to prepare data for getTimeTicks.
  18523. * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
  18524. * of segments in stock charts, the normalizing logic was extracted in order to
  18525. * prevent it for running over again for each segment having the same interval.
  18526. * #662, #697.
  18527. *
  18528. * @private
  18529. * @function Highcharts.Axis#normalizeTimeTickInterval
  18530. * @param {number} tickInterval
  18531. * @param {Array<Array<string,(Array<number>|null)>>} [unitsOption]
  18532. * @return {Highcharts.DateTimeAxisNormalizedObject}
  18533. */
  18534. Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
  18535. var units = unitsOption || [[
  18536. 'millisecond',
  18537. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  18538. ], [
  18539. 'second',
  18540. [1, 2, 5, 10, 15, 30]
  18541. ], [
  18542. 'minute',
  18543. [1, 2, 5, 10, 15, 30]
  18544. ], [
  18545. 'hour',
  18546. [1, 2, 3, 4, 6, 8, 12]
  18547. ], [
  18548. 'day',
  18549. [1, 2]
  18550. ], [
  18551. 'week',
  18552. [1, 2]
  18553. ], [
  18554. 'month',
  18555. [1, 2, 3, 4, 6]
  18556. ], [
  18557. 'year',
  18558. null
  18559. ]], unit = units[units.length - 1], // default unit is years
  18560. interval = timeUnits[unit[0]], multiples = unit[1], count, i;
  18561. // loop through the units to find the one that best fits the tickInterval
  18562. for (i = 0; i < units.length; i++) {
  18563. unit = units[i];
  18564. interval = timeUnits[unit[0]];
  18565. multiples = unit[1];
  18566. if (units[i + 1]) {
  18567. // lessThan is in the middle between the highest multiple and the
  18568. // next unit.
  18569. var lessThan = (interval *
  18570. multiples[multiples.length - 1] +
  18571. timeUnits[units[i + 1][0]]) / 2;
  18572. // break and keep the current unit
  18573. if (tickInterval <= lessThan) {
  18574. break;
  18575. }
  18576. }
  18577. }
  18578. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  18579. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  18580. multiples = [1, 2, 5];
  18581. }
  18582. // get the count
  18583. count = normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ?
  18584. Math.max(getMagnitude(tickInterval / interval), 1) : // #1913, #2360
  18585. 1);
  18586. return {
  18587. unitRange: interval,
  18588. count: count,
  18589. unitName: unit[0]
  18590. };
  18591. };
  18592. });
  18593. _registerModule(_modules, 'parts/LogarithmicAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  18594. /* *
  18595. *
  18596. * (c) 2010-2019 Torstein Honsi
  18597. *
  18598. * License: www.highcharts.com/license
  18599. *
  18600. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  18601. *
  18602. * */
  18603. var pick = U.pick;
  18604. var Axis = H.Axis, getMagnitude = H.getMagnitude, normalizeTickInterval = H.normalizeTickInterval;
  18605. /* ************************************************************************** *
  18606. * Methods defined on the Axis prototype
  18607. * ************************************************************************** */
  18608. /* eslint-disable valid-jsdoc */
  18609. /**
  18610. * Set the tick positions of a logarithmic axis.
  18611. *
  18612. * @private
  18613. * @function Highcharts.Axis#getLogTickPositions
  18614. * @param {number} interval
  18615. * @param {number} min
  18616. * @param {number} max
  18617. * @param {boolean} [minor]
  18618. * @return {Array<number>}
  18619. */
  18620. Axis.prototype.getLogTickPositions = function (interval, min, max, minor) {
  18621. var axis = this, options = axis.options, axisLength = axis.len,
  18622. // Since we use this method for both major and minor ticks,
  18623. // use a local variable and return the result
  18624. positions = [];
  18625. // Reset
  18626. if (!minor) {
  18627. axis._minorAutoInterval = null;
  18628. }
  18629. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  18630. if (interval >= 0.5) {
  18631. interval = Math.round(interval);
  18632. positions = axis.getLinearTickPositions(interval, min, max);
  18633. // Second case: We need intermediary ticks. For example
  18634. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  18635. }
  18636. else if (interval >= 0.08) {
  18637. var roundedMin = Math.floor(min), intermediate, i, j, len, pos, lastPos, break2;
  18638. if (interval > 0.3) {
  18639. intermediate = [1, 2, 4];
  18640. // 0.2 equals five minor ticks per 1, 10, 100 etc
  18641. }
  18642. else if (interval > 0.15) {
  18643. intermediate = [1, 2, 4, 6, 8];
  18644. }
  18645. else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  18646. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  18647. }
  18648. for (i = roundedMin; i < max + 1 && !break2; i++) {
  18649. len = intermediate.length;
  18650. for (j = 0; j < len && !break2; j++) {
  18651. pos = axis.log2lin(axis.lin2log(i) * intermediate[j]);
  18652. // #1670, lastPos is #3113
  18653. if (pos > min &&
  18654. (!minor || lastPos <= max) &&
  18655. typeof lastPos !== 'undefined') {
  18656. positions.push(lastPos);
  18657. }
  18658. if (lastPos > max) {
  18659. break2 = true;
  18660. }
  18661. lastPos = pos;
  18662. }
  18663. }
  18664. // Third case: We are so deep in between whole logarithmic values that
  18665. // we might as well handle the tick positions like a linear axis. For
  18666. // example 1.01, 1.02, 1.03, 1.04.
  18667. }
  18668. else {
  18669. var realMin = axis.lin2log(min), realMax = axis.lin2log(max), tickIntervalOption = minor ?
  18670. this.getMinorTickInterval() :
  18671. options.tickInterval, filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  18672. null :
  18673. tickIntervalOption, tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1), totalPixelLength = minor ?
  18674. axisLength / axis.tickPositions.length :
  18675. axisLength;
  18676. interval = pick(filteredTickIntervalOption, axis._minorAutoInterval, (realMax - realMin) *
  18677. tickPixelIntervalOption / (totalPixelLength || 1));
  18678. interval = normalizeTickInterval(interval, null, getMagnitude(interval));
  18679. positions = axis.getLinearTickPositions(interval, realMin, realMax).map(axis.log2lin);
  18680. if (!minor) {
  18681. axis._minorAutoInterval = interval / 5;
  18682. }
  18683. }
  18684. // Set the axis-level tickInterval variable
  18685. if (!minor) {
  18686. axis.tickInterval = interval;
  18687. }
  18688. return positions;
  18689. };
  18690. /**
  18691. * @private
  18692. * @function Highcharts.Axis#log2lin
  18693. *
  18694. * @param {number} num
  18695. *
  18696. * @return {number}
  18697. */
  18698. Axis.prototype.log2lin = function (num) {
  18699. return Math.log(num) / Math.LN10;
  18700. };
  18701. /**
  18702. * @private
  18703. * @function Highcharts.Axis#lin2log
  18704. *
  18705. * @param {number} num
  18706. *
  18707. * @return {number}
  18708. */
  18709. Axis.prototype.lin2log = function (num) {
  18710. return Math.pow(10, num);
  18711. };
  18712. });
  18713. _registerModule(_modules, 'parts/PlotLineOrBand.js', [_modules['parts/Globals.js'], _modules['parts/Axis.js'], _modules['parts/Utilities.js']], function (H, Axis, U) {
  18714. /* *
  18715. *
  18716. * (c) 2010-2019 Torstein Honsi
  18717. *
  18718. * License: www.highcharts.com/license
  18719. *
  18720. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  18721. *
  18722. * */
  18723. /**
  18724. * Options for plot bands on axes.
  18725. *
  18726. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  18727. */
  18728. /**
  18729. * Options for plot band labels on axes.
  18730. *
  18731. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  18732. */
  18733. /**
  18734. * Options for plot lines on axes.
  18735. *
  18736. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  18737. */
  18738. /**
  18739. * Options for plot line labels on axes.
  18740. *
  18741. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  18742. */
  18743. var arrayMax = U.arrayMax, arrayMin = U.arrayMin, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, objectEach = U.objectEach, pick = U.pick;
  18744. var merge = H.merge;
  18745. /* eslint-disable no-invalid-this, valid-jsdoc */
  18746. /**
  18747. * The object wrapper for plot lines and plot bands
  18748. *
  18749. * @class
  18750. * @name Highcharts.PlotLineOrBand
  18751. *
  18752. * @param {Highcharts.Axis} axis
  18753. *
  18754. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
  18755. */
  18756. H.PlotLineOrBand = function (axis, options) {
  18757. this.axis = axis;
  18758. if (options) {
  18759. this.options = options;
  18760. this.id = options.id;
  18761. }
  18762. };
  18763. H.PlotLineOrBand.prototype = {
  18764. /**
  18765. * Render the plot line or plot band. If it is already existing,
  18766. * move it.
  18767. *
  18768. * @private
  18769. * @function Highcharts.PlotLineOrBand#render
  18770. * @return {Highcharts.PlotLineOrBand|undefined}
  18771. */
  18772. render: function () {
  18773. H.fireEvent(this, 'render');
  18774. var plotLine = this, axis = plotLine.axis, horiz = axis.horiz, 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 = {
  18775. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  18776. (options.className || '')
  18777. }, groupAttribs = {}, renderer = axis.chart.renderer, groupName = isBand ? 'bands' : 'lines', group;
  18778. // logarithmic conversion
  18779. if (axis.isLog) {
  18780. from = axis.log2lin(from);
  18781. to = axis.log2lin(to);
  18782. value = axis.log2lin(value);
  18783. }
  18784. // Set the presentational attributes
  18785. if (!axis.chart.styledMode) {
  18786. if (isLine) {
  18787. attribs.stroke = color || '#999999';
  18788. attribs['stroke-width'] = pick(options.width, 1);
  18789. if (options.dashStyle) {
  18790. attribs.dashstyle =
  18791. options.dashStyle;
  18792. }
  18793. }
  18794. else if (isBand) { // plot band
  18795. attribs.fill = color || '#e6ebf5';
  18796. if (options.borderWidth) {
  18797. attribs.stroke = options.borderColor;
  18798. attribs['stroke-width'] = options.borderWidth;
  18799. }
  18800. }
  18801. }
  18802. // Grouping and zIndex
  18803. groupAttribs.zIndex = zIndex;
  18804. groupName += '-' + zIndex;
  18805. group = axis.plotLinesAndBandsGroups[groupName];
  18806. if (!group) {
  18807. axis.plotLinesAndBandsGroups[groupName] = group =
  18808. renderer.g('plot-' + groupName)
  18809. .attr(groupAttribs).add();
  18810. }
  18811. // Create the path
  18812. if (isNew) {
  18813. /**
  18814. * SVG element of the plot line or band.
  18815. *
  18816. * @name Highcharts.PlotLineOrBand#svgElement
  18817. * @type {Highcharts.SVGElement}
  18818. */
  18819. plotLine.svgElem = svgElem = renderer
  18820. .path()
  18821. .attr(attribs)
  18822. .add(group);
  18823. }
  18824. // Set the path or return
  18825. if (isLine) {
  18826. path = axis.getPlotLinePath({
  18827. value: value,
  18828. lineWidth: svgElem.strokeWidth(),
  18829. acrossPanes: options.acrossPanes
  18830. });
  18831. }
  18832. else if (isBand) { // plot band
  18833. path = axis.getPlotBandPath(from, to, options);
  18834. }
  18835. else {
  18836. return;
  18837. }
  18838. // common for lines and bands
  18839. if ((isNew || !svgElem.d) && path && path.length) {
  18840. svgElem.attr({ d: path });
  18841. // events
  18842. if (events) {
  18843. objectEach(events, function (event, eventType) {
  18844. svgElem.on(eventType, function (e) {
  18845. events[eventType].apply(plotLine, [e]);
  18846. });
  18847. });
  18848. }
  18849. }
  18850. else if (svgElem) {
  18851. if (path) {
  18852. svgElem.show(true);
  18853. svgElem.animate({ d: path });
  18854. }
  18855. else if (svgElem.d) {
  18856. svgElem.hide();
  18857. if (label) {
  18858. plotLine.label = label = label.destroy();
  18859. }
  18860. }
  18861. }
  18862. // the plot band/line label
  18863. if (optionsLabel &&
  18864. (defined(optionsLabel.text) || defined(optionsLabel.formatter)) &&
  18865. path &&
  18866. path.length &&
  18867. axis.width > 0 &&
  18868. axis.height > 0 &&
  18869. !path.isFlat) {
  18870. // apply defaults
  18871. optionsLabel = merge({
  18872. align: horiz && isBand && 'center',
  18873. x: horiz ? !isBand && 4 : 10,
  18874. verticalAlign: !horiz && isBand && 'middle',
  18875. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  18876. rotation: horiz && !isBand && 90
  18877. }, optionsLabel);
  18878. this.renderLabel(optionsLabel, path, isBand, zIndex);
  18879. }
  18880. else if (label) { // move out of sight
  18881. label.hide();
  18882. }
  18883. // chainable
  18884. return plotLine;
  18885. },
  18886. /**
  18887. * Render and align label for plot line or band.
  18888. *
  18889. * @private
  18890. * @function Highcharts.PlotLineOrBand#renderLabel
  18891. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  18892. * @param {Highcharts.SVGPathArray} path
  18893. * @param {boolean} [isBand]
  18894. * @param {number} [zIndex]
  18895. * @return {void}
  18896. */
  18897. renderLabel: function (optionsLabel, path, isBand, zIndex) {
  18898. var plotLine = this, label = plotLine.label, renderer = plotLine.axis.chart.renderer, attribs, xBounds, yBounds, x, y, labelText;
  18899. // add the SVG element
  18900. if (!label) {
  18901. attribs = {
  18902. align: optionsLabel.textAlign || optionsLabel.align,
  18903. rotation: optionsLabel.rotation,
  18904. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  18905. '-label ' + (optionsLabel.className || '')
  18906. };
  18907. attribs.zIndex = zIndex;
  18908. labelText = this.getLabelText(optionsLabel);
  18909. /**
  18910. * SVG element of the label.
  18911. *
  18912. * @name Highcharts.PlotLineOrBand#label
  18913. * @type {Highcharts.SVGElement}
  18914. */
  18915. plotLine.label = label = renderer
  18916. .text(labelText, 0, 0, optionsLabel.useHTML)
  18917. .attr(attribs)
  18918. .add();
  18919. if (!this.axis.chart.styledMode) {
  18920. label.css(optionsLabel.style);
  18921. }
  18922. }
  18923. // get the bounding box and align the label
  18924. // #3000 changed to better handle choice between plotband or plotline
  18925. xBounds = path.xBounds ||
  18926. [path[1], path[4], (isBand ? path[6] : path[1])];
  18927. yBounds = path.yBounds ||
  18928. [path[2], path[5], (isBand ? path[7] : path[2])];
  18929. x = arrayMin(xBounds);
  18930. y = arrayMin(yBounds);
  18931. label.align(optionsLabel, false, {
  18932. x: x,
  18933. y: y,
  18934. width: arrayMax(xBounds) - x,
  18935. height: arrayMax(yBounds) - y
  18936. });
  18937. label.show(true);
  18938. },
  18939. /**
  18940. * Get label's text content.
  18941. *
  18942. * @private
  18943. * @function Highcharts.PlotLineOrBand#getLabelText
  18944. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  18945. * @return {string}
  18946. */
  18947. getLabelText: function (optionsLabel) {
  18948. return defined(optionsLabel.formatter) ?
  18949. optionsLabel.formatter
  18950. .call(this) :
  18951. optionsLabel.text;
  18952. },
  18953. /**
  18954. * Remove the plot line or band.
  18955. *
  18956. * @function Highcharts.PlotLineOrBand#destroy
  18957. * @return {void}
  18958. */
  18959. destroy: function () {
  18960. // remove it from the lookup
  18961. erase(this.axis.plotLinesAndBands, this);
  18962. delete this.axis;
  18963. destroyObjectProperties(this);
  18964. }
  18965. };
  18966. /* eslint-enable no-invalid-this, valid-jsdoc */
  18967. // Object with members for extending the Axis prototype
  18968. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  18969. /**
  18970. * An array of colored bands stretching across the plot area marking an
  18971. * interval on the axis.
  18972. *
  18973. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  18974. * class in addition to the `className` option.
  18975. *
  18976. * @productdesc {highcharts}
  18977. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  18978. * perimeter of the gauge.
  18979. *
  18980. * @type {Array<*>}
  18981. * @product highcharts highstock gantt
  18982. * @apioption xAxis.plotBands
  18983. */
  18984. /**
  18985. * Flag to decide if plotBand should be rendered across all panes.
  18986. *
  18987. * @since 7.1.2
  18988. * @product highstock
  18989. * @type {boolean}
  18990. * @default true
  18991. * @apioption xAxis.plotBands.acrossPanes
  18992. */
  18993. /**
  18994. * Border color for the plot band. Also requires `borderWidth` to be set.
  18995. *
  18996. * @type {Highcharts.ColorString}
  18997. * @apioption xAxis.plotBands.borderColor
  18998. */
  18999. /**
  19000. * Border width for the plot band. Also requires `borderColor` to be set.
  19001. *
  19002. * @type {number}
  19003. * @default 0
  19004. * @apioption xAxis.plotBands.borderWidth
  19005. */
  19006. /**
  19007. * A custom class name, in addition to the default `highcharts-plot-band`,
  19008. * to apply to each individual band.
  19009. *
  19010. * @type {string}
  19011. * @since 5.0.0
  19012. * @apioption xAxis.plotBands.className
  19013. */
  19014. /**
  19015. * The color of the plot band.
  19016. *
  19017. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19018. * Color band
  19019. * @sample {highstock} stock/xaxis/plotbands/
  19020. * Plot band on Y axis
  19021. *
  19022. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  19023. * @default #e6ebf5
  19024. * @apioption xAxis.plotBands.color
  19025. */
  19026. /**
  19027. * An object defining mouse events for the plot band. Supported properties
  19028. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  19029. *
  19030. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  19031. * Mouse events demonstrated
  19032. *
  19033. * @since 1.2
  19034. * @apioption xAxis.plotBands.events
  19035. */
  19036. /**
  19037. * Click event on a plot band.
  19038. *
  19039. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19040. * @apioption xAxis.plotBands.events.click
  19041. */
  19042. /**
  19043. * Mouse move event on a plot band.
  19044. *
  19045. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19046. * @apioption xAxis.plotBands.events.mousemove
  19047. */
  19048. /**
  19049. * Mouse out event on the corner of a plot band.
  19050. *
  19051. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19052. * @apioption xAxis.plotBands.events.mouseout
  19053. */
  19054. /**
  19055. * Mouse over event on a plot band.
  19056. *
  19057. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19058. * @apioption xAxis.plotBands.events.mouseover
  19059. */
  19060. /**
  19061. * The start position of the plot band in axis units.
  19062. *
  19063. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19064. * Datetime axis
  19065. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  19066. * Categorized axis
  19067. * @sample {highstock} stock/xaxis/plotbands/
  19068. * Plot band on Y axis
  19069. *
  19070. * @type {number}
  19071. * @apioption xAxis.plotBands.from
  19072. */
  19073. /**
  19074. * An id used for identifying the plot band in Axis.removePlotBand.
  19075. *
  19076. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  19077. * Remove plot band by id
  19078. * @sample {highstock} highcharts/xaxis/plotbands-id/
  19079. * Remove plot band by id
  19080. *
  19081. * @type {string}
  19082. * @apioption xAxis.plotBands.id
  19083. */
  19084. /**
  19085. * The end position of the plot band in axis units.
  19086. *
  19087. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19088. * Datetime axis
  19089. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  19090. * Categorized axis
  19091. * @sample {highstock} stock/xaxis/plotbands/
  19092. * Plot band on Y axis
  19093. *
  19094. * @type {number}
  19095. * @apioption xAxis.plotBands.to
  19096. */
  19097. /**
  19098. * The z index of the plot band within the chart, relative to other
  19099. * elements. Using the same z index as another element may give
  19100. * unpredictable results, as the last rendered element will be on top.
  19101. * Values from 0 to 20 make sense.
  19102. *
  19103. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19104. * Behind plot lines by default
  19105. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  19106. * Above plot lines
  19107. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  19108. * Above plot lines and series
  19109. *
  19110. * @type {number}
  19111. * @since 1.2
  19112. * @apioption xAxis.plotBands.zIndex
  19113. */
  19114. /**
  19115. * Text labels for the plot bands
  19116. *
  19117. * @product highcharts highstock gantt
  19118. * @apioption xAxis.plotBands.label
  19119. */
  19120. /**
  19121. * Horizontal alignment of the label. Can be one of "left", "center" or
  19122. * "right".
  19123. *
  19124. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  19125. * Aligned to the right
  19126. * @sample {highstock} stock/xaxis/plotbands-label/
  19127. * Plot band with labels
  19128. *
  19129. * @type {Highcharts.AlignValue}
  19130. * @default center
  19131. * @since 2.1
  19132. * @apioption xAxis.plotBands.label.align
  19133. */
  19134. /**
  19135. * Rotation of the text label in degrees .
  19136. *
  19137. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  19138. * Vertical text
  19139. *
  19140. * @type {number}
  19141. * @default 0
  19142. * @since 2.1
  19143. * @apioption xAxis.plotBands.label.rotation
  19144. */
  19145. /**
  19146. * CSS styles for the text label.
  19147. *
  19148. * In styled mode, the labels are styled by the
  19149. * `.highcharts-plot-band-label` class.
  19150. *
  19151. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  19152. * Blue and bold label
  19153. *
  19154. * @type {Highcharts.CSSObject}
  19155. * @since 2.1
  19156. * @apioption xAxis.plotBands.label.style
  19157. */
  19158. /**
  19159. * The string text itself. A subset of HTML is supported.
  19160. *
  19161. * @type {string}
  19162. * @since 2.1
  19163. * @apioption xAxis.plotBands.label.text
  19164. */
  19165. /**
  19166. * The text alignment for the label. While `align` determines where the
  19167. * texts anchor point is placed within the plot band, `textAlign` determines
  19168. * how the text is aligned against its anchor point. Possible values are
  19169. * "left", "center" and "right". Defaults to the same as the `align` option.
  19170. *
  19171. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  19172. * Vertical text in center position but text-aligned left
  19173. *
  19174. * @type {Highcharts.AlignValue}
  19175. * @since 2.1
  19176. * @apioption xAxis.plotBands.label.textAlign
  19177. */
  19178. /**
  19179. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19180. * to render the labels.
  19181. *
  19182. * @type {boolean}
  19183. * @default false
  19184. * @since 3.0.3
  19185. * @apioption xAxis.plotBands.label.useHTML
  19186. */
  19187. /**
  19188. * Vertical alignment of the label relative to the plot band. Can be one of
  19189. * "top", "middle" or "bottom".
  19190. *
  19191. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  19192. * Vertically centered label
  19193. * @sample {highstock} stock/xaxis/plotbands-label/
  19194. * Plot band with labels
  19195. *
  19196. * @type {Highcharts.VerticalAlignValue}
  19197. * @default top
  19198. * @since 2.1
  19199. * @apioption xAxis.plotBands.label.verticalAlign
  19200. */
  19201. /**
  19202. * Horizontal position relative the alignment. Default varies by
  19203. * orientation.
  19204. *
  19205. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  19206. * Aligned 10px from the right edge
  19207. * @sample {highstock} stock/xaxis/plotbands-label/
  19208. * Plot band with labels
  19209. *
  19210. * @type {number}
  19211. * @since 2.1
  19212. * @apioption xAxis.plotBands.label.x
  19213. */
  19214. /**
  19215. * Vertical position of the text baseline relative to the alignment. Default
  19216. * varies by orientation.
  19217. *
  19218. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  19219. * Label on x axis
  19220. * @sample {highstock} stock/xaxis/plotbands-label/
  19221. * Plot band with labels
  19222. *
  19223. * @type {number}
  19224. * @since 2.1
  19225. * @apioption xAxis.plotBands.label.y
  19226. */
  19227. /**
  19228. * An array of lines stretching across the plot area, marking a specific
  19229. * value on one of the axes.
  19230. *
  19231. * In styled mode, the plot lines are styled by the
  19232. * `.highcharts-plot-line` class in addition to the `className` option.
  19233. *
  19234. * @type {Array<*>}
  19235. * @product highcharts highstock gantt
  19236. * @apioption xAxis.plotLines
  19237. */
  19238. /**
  19239. * Flag to decide if plotLine should be rendered across all panes.
  19240. *
  19241. * @sample {highstock} stock/xaxis/plotlines-acrosspanes/
  19242. * Plot lines on different panes
  19243. *
  19244. * @since 7.1.2
  19245. * @product highstock
  19246. * @type {boolean}
  19247. * @default true
  19248. * @apioption xAxis.plotLines.acrossPanes
  19249. */
  19250. /**
  19251. * A custom class name, in addition to the default `highcharts-plot-line`,
  19252. * to apply to each individual line.
  19253. *
  19254. * @type {string}
  19255. * @since 5.0.0
  19256. * @apioption xAxis.plotLines.className
  19257. */
  19258. /**
  19259. * The color of the line.
  19260. *
  19261. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19262. * A red line from X axis
  19263. * @sample {highstock} stock/xaxis/plotlines/
  19264. * Plot line on Y axis
  19265. *
  19266. * @type {Highcharts.ColorString}
  19267. * @default #999999
  19268. * @apioption xAxis.plotLines.color
  19269. */
  19270. /**
  19271. * The dashing or dot style for the plot line. For possible values see
  19272. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  19273. *
  19274. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  19275. * Dash and dot pattern
  19276. * @sample {highstock} stock/xaxis/plotlines/
  19277. * Plot line on Y axis
  19278. *
  19279. * @type {Highcharts.DashStyleValue}
  19280. * @default Solid
  19281. * @since 1.2
  19282. * @apioption xAxis.plotLines.dashStyle
  19283. */
  19284. /**
  19285. * An object defining mouse events for the plot line. Supported
  19286. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  19287. *
  19288. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  19289. * Mouse events demonstrated
  19290. *
  19291. * @since 1.2
  19292. * @apioption xAxis.plotLines.events
  19293. */
  19294. /**
  19295. * Click event on a plot band.
  19296. *
  19297. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19298. * @apioption xAxis.plotLines.events.click
  19299. */
  19300. /**
  19301. * Mouse move event on a plot band.
  19302. *
  19303. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19304. * @apioption xAxis.plotLines.events.mousemove
  19305. */
  19306. /**
  19307. * Mouse out event on the corner of a plot band.
  19308. *
  19309. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19310. * @apioption xAxis.plotLines.events.mouseout
  19311. */
  19312. /**
  19313. * Mouse over event on a plot band.
  19314. *
  19315. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  19316. * @apioption xAxis.plotLines.events.mouseover
  19317. */
  19318. /**
  19319. * An id used for identifying the plot line in Axis.removePlotLine.
  19320. *
  19321. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  19322. * Remove plot line by id
  19323. *
  19324. * @type {string}
  19325. * @apioption xAxis.plotLines.id
  19326. */
  19327. /**
  19328. * The position of the line in axis units.
  19329. *
  19330. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19331. * Between two categories on X axis
  19332. * @sample {highstock} stock/xaxis/plotlines/
  19333. * Plot line on Y axis
  19334. *
  19335. * @type {number}
  19336. * @apioption xAxis.plotLines.value
  19337. */
  19338. /**
  19339. * The width or thickness of the plot line.
  19340. *
  19341. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19342. * 2px wide line from X axis
  19343. * @sample {highstock} stock/xaxis/plotlines/
  19344. * Plot line on Y axis
  19345. *
  19346. * @type {number}
  19347. * @default 2
  19348. * @apioption xAxis.plotLines.width
  19349. */
  19350. /**
  19351. * The z index of the plot line within the chart.
  19352. *
  19353. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  19354. * Behind plot lines by default
  19355. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  19356. * Above plot lines
  19357. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  19358. * Above plot lines and series
  19359. *
  19360. * @type {number}
  19361. * @since 1.2
  19362. * @apioption xAxis.plotLines.zIndex
  19363. */
  19364. /**
  19365. * Text labels for the plot bands
  19366. *
  19367. * @apioption xAxis.plotLines.label
  19368. */
  19369. /**
  19370. * Horizontal alignment of the label. Can be one of "left", "center" or
  19371. * "right".
  19372. *
  19373. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  19374. * Aligned to the right
  19375. * @sample {highstock} stock/xaxis/plotlines/
  19376. * Plot line on Y axis
  19377. *
  19378. * @type {Highcharts.AlignValue}
  19379. * @default left
  19380. * @since 2.1
  19381. * @apioption xAxis.plotLines.label.align
  19382. */
  19383. /**
  19384. * Callback JavaScript function to format the label. Useful properties like
  19385. * the value of plot line or the range of plot band (`from` & `to`
  19386. * properties) can be found in `this.options` object.
  19387. *
  19388. * @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
  19389. * Label formatters for plot line and plot band.
  19390. * @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
  19391. * @apioption xAxis.plotLines.label.formatter
  19392. */
  19393. /**
  19394. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  19395. * lines and 90 for vertical lines.
  19396. *
  19397. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  19398. * Slanted text
  19399. *
  19400. * @type {number}
  19401. * @since 2.1
  19402. * @apioption xAxis.plotLines.label.rotation
  19403. */
  19404. /**
  19405. * CSS styles for the text label.
  19406. *
  19407. * In styled mode, the labels are styled by the
  19408. * `.highcharts-plot-line-label` class.
  19409. *
  19410. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  19411. * Blue and bold label
  19412. *
  19413. * @type {Highcharts.CSSObject}
  19414. * @since 2.1
  19415. * @apioption xAxis.plotLines.label.style
  19416. */
  19417. /**
  19418. * The text itself. A subset of HTML is supported.
  19419. *
  19420. * @type {string}
  19421. * @since 2.1
  19422. * @apioption xAxis.plotLines.label.text
  19423. */
  19424. /**
  19425. * The text alignment for the label. While `align` determines where the
  19426. * texts anchor point is placed within the plot band, `textAlign` determines
  19427. * how the text is aligned against its anchor point. Possible values are
  19428. * "left", "center" and "right". Defaults to the same as the `align` option.
  19429. *
  19430. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  19431. * Text label in bottom position
  19432. *
  19433. * @type {Highcharts.AlignValue}
  19434. * @since 2.1
  19435. * @apioption xAxis.plotLines.label.textAlign
  19436. */
  19437. /**
  19438. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19439. * to render the labels.
  19440. *
  19441. * @type {boolean}
  19442. * @default false
  19443. * @since 3.0.3
  19444. * @apioption xAxis.plotLines.label.useHTML
  19445. */
  19446. /**
  19447. * Vertical alignment of the label relative to the plot line. Can be
  19448. * one of "top", "middle" or "bottom".
  19449. *
  19450. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  19451. * Vertically centered label
  19452. *
  19453. * @type {Highcharts.VerticalAlignValue}
  19454. * @default {highcharts} top
  19455. * @default {highstock} top
  19456. * @since 2.1
  19457. * @apioption xAxis.plotLines.label.verticalAlign
  19458. */
  19459. /**
  19460. * Horizontal position relative the alignment. Default varies by
  19461. * orientation.
  19462. *
  19463. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  19464. * Aligned 10px from the right edge
  19465. * @sample {highstock} stock/xaxis/plotlines/
  19466. * Plot line on Y axis
  19467. *
  19468. * @type {number}
  19469. * @since 2.1
  19470. * @apioption xAxis.plotLines.label.x
  19471. */
  19472. /**
  19473. * Vertical position of the text baseline relative to the alignment. Default
  19474. * varies by orientation.
  19475. *
  19476. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  19477. * Label below the plot line
  19478. * @sample {highstock} stock/xaxis/plotlines/
  19479. * Plot line on Y axis
  19480. *
  19481. * @type {number}
  19482. * @since 2.1
  19483. * @apioption xAxis.plotLines.label.y
  19484. */
  19485. /**
  19486. * An array of objects defining plot bands on the Y axis.
  19487. *
  19488. * @type {Array<*>}
  19489. * @extends xAxis.plotBands
  19490. * @apioption yAxis.plotBands
  19491. */
  19492. /**
  19493. * In a gauge chart, this option determines the inner radius of the
  19494. * plot band that stretches along the perimeter. It can be given as
  19495. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  19496. * By default, the inner radius is controlled by the [thickness](
  19497. * #yAxis.plotBands.thickness) option.
  19498. *
  19499. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19500. * Gauge plot band
  19501. *
  19502. * @type {number|string}
  19503. * @since 2.3
  19504. * @product highcharts
  19505. * @apioption yAxis.plotBands.innerRadius
  19506. */
  19507. /**
  19508. * In a gauge chart, this option determines the outer radius of the
  19509. * plot band that stretches along the perimeter. It can be given as
  19510. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  19511. *
  19512. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19513. * Gauge plot band
  19514. *
  19515. * @type {number|string}
  19516. * @default 100%
  19517. * @since 2.3
  19518. * @product highcharts
  19519. * @apioption yAxis.plotBands.outerRadius
  19520. */
  19521. /**
  19522. * In a gauge chart, this option sets the width of the plot band
  19523. * stretching along the perimeter. It can be given as a percentage
  19524. * string, like `"10%"`, or as a pixel number, like `10`. The default
  19525. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  19526. * thus making the plot band act as a background for the tick markers.
  19527. *
  19528. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19529. * Gauge plot band
  19530. *
  19531. * @type {number|string}
  19532. * @default 10
  19533. * @since 2.3
  19534. * @product highcharts
  19535. * @apioption yAxis.plotBands.thickness
  19536. */
  19537. /**
  19538. * An array of objects representing plot lines on the X axis
  19539. *
  19540. * @type {Array<*>}
  19541. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19542. * Basic plot line
  19543. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  19544. * Solid gauge plot line
  19545. * @extends xAxis.plotLines
  19546. * @apioption yAxis.plotLines
  19547. */
  19548. /* eslint-disable no-invalid-this, valid-jsdoc */
  19549. /**
  19550. * Internal function to create the SVG path definition for a plot band.
  19551. *
  19552. * @function Highcharts.Axis#getPlotBandPath
  19553. *
  19554. * @param {number} from
  19555. * The axis value to start from.
  19556. *
  19557. * @param {number} to
  19558. * The axis value to end on.
  19559. *
  19560. * @return {Highcharts.SVGPathArray}
  19561. * The SVG path definition in array form.
  19562. */
  19563. getPlotBandPath: function (from, to) {
  19564. var toPath = this.getPlotLinePath({
  19565. value: to,
  19566. force: true,
  19567. acrossPanes: this.options.acrossPanes
  19568. }), path = this.getPlotLinePath({
  19569. value: from,
  19570. force: true,
  19571. acrossPanes: this.options.acrossPanes
  19572. }), result = [], i,
  19573. // #4964 check if chart is inverted or plotband is on yAxis
  19574. horiz = this.horiz, plus = 1, isFlat, outside = (from < this.min && to < this.min) ||
  19575. (from > this.max && to > this.max);
  19576. if (path && toPath) {
  19577. // Flat paths don't need labels (#3836)
  19578. if (outside) {
  19579. isFlat = path.toString() === toPath.toString();
  19580. plus = 0;
  19581. }
  19582. // Go over each subpath - for panes in Highstock
  19583. for (i = 0; i < path.length; i += 6) {
  19584. // Add 1 pixel when coordinates are the same
  19585. if (horiz && toPath[i + 1] === path[i + 1]) {
  19586. toPath[i + 1] += plus;
  19587. toPath[i + 4] += plus;
  19588. }
  19589. else if (!horiz && toPath[i + 2] === path[i + 2]) {
  19590. toPath[i + 2] += plus;
  19591. toPath[i + 5] += plus;
  19592. }
  19593. result.push('M', path[i + 1], path[i + 2], 'L', path[i + 4], path[i + 5], toPath[i + 4], toPath[i + 5], toPath[i + 1], toPath[i + 2], 'z');
  19594. result.isFlat = isFlat;
  19595. }
  19596. }
  19597. else { // outside the axis area
  19598. path = null;
  19599. }
  19600. return result;
  19601. },
  19602. /**
  19603. * Add a plot band after render time.
  19604. *
  19605. * @sample highcharts/members/axis-addplotband/
  19606. * Toggle the plot band from a button
  19607. *
  19608. * @function Highcharts.Axis#addPlotBand
  19609. *
  19610. * @param {Highcharts.AxisPlotBandsOptions} options
  19611. * A configuration object for the plot band, as defined in
  19612. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  19613. *
  19614. * @return {Highcharts.PlotLineOrBand|undefined}
  19615. * The added plot band.
  19616. */
  19617. addPlotBand: function (options) {
  19618. return this.addPlotBandOrLine(options, 'plotBands');
  19619. },
  19620. /**
  19621. * Add a plot line after render time.
  19622. *
  19623. * @sample highcharts/members/axis-addplotline/
  19624. * Toggle the plot line from a button
  19625. *
  19626. * @function Highcharts.Axis#addPlotLine
  19627. *
  19628. * @param {Highcharts.AxisPlotLinesOptions} options
  19629. * A configuration object for the plot line, as defined in
  19630. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  19631. *
  19632. * @return {Highcharts.PlotLineOrBand|undefined}
  19633. * The added plot line.
  19634. */
  19635. addPlotLine: function (options) {
  19636. return this.addPlotBandOrLine(options, 'plotLines');
  19637. },
  19638. /**
  19639. * Add a plot band or plot line after render time. Called from addPlotBand
  19640. * and addPlotLine internally.
  19641. *
  19642. * @private
  19643. * @function Highcharts.Axis#addPlotBandOrLine
  19644. *
  19645. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  19646. * The plotBand or plotLine configuration object.
  19647. *
  19648. * @param {"plotBands"|"plotLines"} [coll]
  19649. *
  19650. * @return {Highcharts.PlotLineOrBand|undefined}
  19651. */
  19652. addPlotBandOrLine: function (options, coll) {
  19653. var obj = new H.PlotLineOrBand(this, options).render(), userOptions = this.userOptions;
  19654. if (obj) { // #2189
  19655. // Add it to the user options for exporting and Axis.update
  19656. if (coll) {
  19657. // Workaround Microsoft/TypeScript issue #32693
  19658. var updatedOptions = (userOptions[coll] || []);
  19659. updatedOptions.push(options);
  19660. userOptions[coll] = updatedOptions;
  19661. }
  19662. this.plotLinesAndBands.push(obj);
  19663. }
  19664. return obj;
  19665. },
  19666. /**
  19667. * Remove a plot band or plot line from the chart by id. Called internally
  19668. * from `removePlotBand` and `removePlotLine`.
  19669. *
  19670. * @private
  19671. * @function Highcharts.Axis#removePlotBandOrLine
  19672. * @param {string} id
  19673. * @return {void}
  19674. */
  19675. removePlotBandOrLine: function (id) {
  19676. var plotLinesAndBands = this.plotLinesAndBands, options = this.options, userOptions = this.userOptions, i = plotLinesAndBands.length;
  19677. while (i--) {
  19678. if (plotLinesAndBands[i].id === id) {
  19679. plotLinesAndBands[i].destroy();
  19680. }
  19681. }
  19682. ([
  19683. options.plotLines || [],
  19684. userOptions.plotLines || [],
  19685. options.plotBands || [],
  19686. userOptions.plotBands || []
  19687. ]).forEach(function (arr) {
  19688. i = arr.length;
  19689. while (i--) {
  19690. if (arr[i].id === id) {
  19691. erase(arr, arr[i]);
  19692. }
  19693. }
  19694. });
  19695. },
  19696. /**
  19697. * Remove a plot band by its id.
  19698. *
  19699. * @sample highcharts/members/axis-removeplotband/
  19700. * Remove plot band by id
  19701. * @sample highcharts/members/axis-addplotband/
  19702. * Toggle the plot band from a button
  19703. *
  19704. * @function Highcharts.Axis#removePlotBand
  19705. *
  19706. * @param {string} id
  19707. * The plot band's `id` as given in the original configuration
  19708. * object or in the `addPlotBand` option.
  19709. *
  19710. * @return {void}
  19711. */
  19712. removePlotBand: function (id) {
  19713. this.removePlotBandOrLine(id);
  19714. },
  19715. /**
  19716. * Remove a plot line by its id.
  19717. *
  19718. * @sample highcharts/xaxis/plotlines-id/
  19719. * Remove plot line by id
  19720. * @sample highcharts/members/axis-addplotline/
  19721. * Toggle the plot line from a button
  19722. *
  19723. * @function Highcharts.Axis#removePlotLine
  19724. *
  19725. * @param {string} id
  19726. * The plot line's `id` as given in the original configuration
  19727. * object or in the `addPlotLine` option.
  19728. */
  19729. removePlotLine: function (id) {
  19730. this.removePlotBandOrLine(id);
  19731. }
  19732. });
  19733. });
  19734. _registerModule(_modules, 'parts/Tooltip.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  19735. /* *
  19736. *
  19737. * (c) 2010-2019 Torstein Honsi
  19738. *
  19739. * License: www.highcharts.com/license
  19740. *
  19741. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  19742. *
  19743. * */
  19744. var clamp = U.clamp, defined = U.defined, discardElement = U.discardElement, extend = U.extend, isNumber = U.isNumber, isString = U.isString, pick = U.pick, splat = U.splat, syncTimeout = U.syncTimeout;
  19745. /**
  19746. * Callback function to format the text of the tooltip from scratch.
  19747. *
  19748. * In case of single or shared tooltips, a string should be be returned. In case
  19749. * of splitted tooltips, it should return an array where the first item is the
  19750. * header, and subsequent items are mapped to the points. Return `false` to
  19751. * disable tooltip for a specific point on series.
  19752. *
  19753. * @callback Highcharts.TooltipFormatterCallbackFunction
  19754. *
  19755. * @param {Highcharts.TooltipFormatterContextObject} this
  19756. * Context to format
  19757. *
  19758. * @param {Highcharts.Tooltip} tooltip
  19759. * The tooltip instance
  19760. *
  19761. * @return {false|string|Array<(string|null|undefined)>|null|undefined}
  19762. * Formatted text or false
  19763. */
  19764. /**
  19765. * @interface Highcharts.TooltipFormatterContextObject
  19766. */ /**
  19767. * @name Highcharts.TooltipFormatterContextObject#color
  19768. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  19769. */ /**
  19770. * @name Highcharts.TooltipFormatterContextObject#colorIndex
  19771. * @type {number|undefined}
  19772. */ /**
  19773. * @name Highcharts.TooltipFormatterContextObject#key
  19774. * @type {number}
  19775. */ /**
  19776. * @name Highcharts.TooltipFormatterContextObject#percentage
  19777. * @type {number|undefined}
  19778. */ /**
  19779. * @name Highcharts.TooltipFormatterContextObject#point
  19780. * @type {Highcharts.Point}
  19781. */ /**
  19782. * @name Highcharts.TooltipFormatterContextObject#points
  19783. * @type {Array<Highcharts.TooltipFormatterContextObject>|undefined}
  19784. */ /**
  19785. * @name Highcharts.TooltipFormatterContextObject#series
  19786. * @type {Highcharts.Series}
  19787. */ /**
  19788. * @name Highcharts.TooltipFormatterContextObject#total
  19789. * @type {number|undefined}
  19790. */ /**
  19791. * @name Highcharts.TooltipFormatterContextObject#x
  19792. * @type {number}
  19793. */ /**
  19794. * @name Highcharts.TooltipFormatterContextObject#y
  19795. * @type {number}
  19796. */
  19797. /**
  19798. * A callback function to place the tooltip in a specific position.
  19799. *
  19800. * @callback Highcharts.TooltipPositionerCallbackFunction
  19801. *
  19802. * @param {number} labelWidth
  19803. * Width of the tooltip.
  19804. *
  19805. * @param {number} labelHeight
  19806. * Height of the tooltip.
  19807. *
  19808. * @param {Highcharts.TooltipPositionerPointObject} point
  19809. * Point information for positioning a tooltip.
  19810. *
  19811. * @return {Highcharts.PositionObject}
  19812. * New position for the tooltip.
  19813. */
  19814. /**
  19815. * Point information for positioning a tooltip.
  19816. *
  19817. * @interface Highcharts.TooltipPositionerPointObject
  19818. */ /**
  19819. * If `tooltip.split` option is enabled and positioner is called for each of the
  19820. * boxes separately, this property indicates the call on the xAxis header, which
  19821. * is not a point itself.
  19822. * @name Highcharts.TooltipPositionerPointObject#isHeader
  19823. * @type {boolean}
  19824. */ /**
  19825. * @name Highcharts.TooltipPositionerPointObject#negative
  19826. * @type {boolean}
  19827. */ /**
  19828. * The reference point relative to the plot area. Add chart.plotLeft to get the
  19829. * full coordinates.
  19830. * @name Highcharts.TooltipPositionerPointObject#plotX
  19831. * @type {number}
  19832. */ /**
  19833. * The reference point relative to the plot area. Add chart.plotTop to get the
  19834. * full coordinates.
  19835. * @name Highcharts.TooltipPositionerPointObject#plotY
  19836. * @type {number}
  19837. */
  19838. /**
  19839. * @typedef {"callout"|"circle"|"square"} Highcharts.TooltipShapeValue
  19840. */
  19841. ''; // separates doclets above from variables below
  19842. var doc = H.doc, format = H.format, merge = H.merge, timeUnits = H.timeUnits;
  19843. /* eslint-disable no-invalid-this, valid-jsdoc */
  19844. /**
  19845. * Tooltip of a chart.
  19846. *
  19847. * @class
  19848. * @name Highcharts.Tooltip
  19849. *
  19850. * @param {Highcharts.Chart} chart
  19851. * The chart instance.
  19852. *
  19853. * @param {Highcharts.TooltipOptions} options
  19854. * Tooltip options.
  19855. */
  19856. H.Tooltip = function () {
  19857. this.init.apply(this, arguments);
  19858. };
  19859. H.Tooltip.prototype = {
  19860. /**
  19861. * @private
  19862. * @function Highcharts.Tooltip#init
  19863. *
  19864. * @param {Highcharts.Chart} chart
  19865. * The chart instance.
  19866. *
  19867. * @param {Highcharts.TooltipOptions} options
  19868. * Tooltip options.
  19869. *
  19870. * @return {void}
  19871. */
  19872. init: function (chart, options) {
  19873. /**
  19874. * Chart of the tooltip.
  19875. *
  19876. * @readonly
  19877. * @name Highcharts.Tooltip#chart
  19878. * @type {Highcharts.Chart}
  19879. */
  19880. this.chart = chart;
  19881. /**
  19882. * Used tooltip options.
  19883. *
  19884. * @readonly
  19885. * @name Highcharts.Tooltip#options
  19886. * @type {Highcharts.TooltipOptions}
  19887. */
  19888. this.options = options;
  19889. /**
  19890. * List of crosshairs.
  19891. *
  19892. * @private
  19893. * @readonly
  19894. * @name Highcharts.Tooltip#crosshairs
  19895. * @type {Array<*>}
  19896. */
  19897. this.crosshairs = [];
  19898. /**
  19899. * Current values of x and y when animating.
  19900. *
  19901. * @private
  19902. * @readonly
  19903. * @name Highcharts.Tooltip#now
  19904. * @type {Highcharts.PositionObject}
  19905. */
  19906. this.now = { x: 0, y: 0 };
  19907. /**
  19908. * Tooltips are initially hidden.
  19909. *
  19910. * @private
  19911. * @readonly
  19912. * @name Highcharts.Tooltip#isHidden
  19913. * @type {boolean}
  19914. */
  19915. this.isHidden = true;
  19916. /**
  19917. * True, if the tooltip is split into one label per series, with the
  19918. * header close to the axis.
  19919. *
  19920. * @readonly
  19921. * @name Highcharts.Tooltip#split
  19922. * @type {boolean|undefined}
  19923. */
  19924. this.split = options.split && !chart.inverted && !chart.polar;
  19925. /**
  19926. * When the tooltip is shared, the entire plot area will capture mouse
  19927. * movement or touch events.
  19928. *
  19929. * @readonly
  19930. * @name Highcharts.Tooltip#shared
  19931. * @type {boolean|undefined}
  19932. */
  19933. this.shared = options.shared || this.split;
  19934. /**
  19935. * Whether to allow the tooltip to render outside the chart's SVG
  19936. * element box. By default (false), the tooltip is rendered within the
  19937. * chart's SVG element, which results in the tooltip being aligned
  19938. * inside the chart area.
  19939. *
  19940. * @readonly
  19941. * @name Highcharts.Tooltip#outside
  19942. * @type {boolean}
  19943. *
  19944. * @todo
  19945. * Split tooltip does not support outside in the first iteration. Should
  19946. * not be too complicated to implement.
  19947. */
  19948. this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
  19949. },
  19950. /**
  19951. * Destroy the single tooltips in a split tooltip.
  19952. * If the tooltip is active then it is not destroyed, unless forced to.
  19953. *
  19954. * @private
  19955. * @function Highcharts.Tooltip#cleanSplit
  19956. *
  19957. * @param {boolean} [force]
  19958. * Force destroy all tooltips.
  19959. *
  19960. * @return {void}
  19961. */
  19962. cleanSplit: function (force) {
  19963. this.chart.series.forEach(function (series) {
  19964. var tt = series && series.tt;
  19965. if (tt) {
  19966. if (!tt.isActive || force) {
  19967. series.tt = tt.destroy();
  19968. }
  19969. else {
  19970. tt.isActive = false;
  19971. }
  19972. }
  19973. });
  19974. },
  19975. /**
  19976. * In styled mode, apply the default filter for the tooltip drop-shadow. It
  19977. * needs to have an id specific to the chart, otherwise there will be issues
  19978. * when one tooltip adopts the filter of a different chart, specifically one
  19979. * where the container is hidden.
  19980. *
  19981. * @private
  19982. * @function Highcharts.Tooltip#applyFilter
  19983. * @return {void}
  19984. */
  19985. applyFilter: function () {
  19986. var chart = this.chart;
  19987. chart.renderer.definition({
  19988. tagName: 'filter',
  19989. id: 'drop-shadow-' + chart.index,
  19990. opacity: 0.5,
  19991. children: [{
  19992. tagName: 'feGaussianBlur',
  19993. 'in': 'SourceAlpha',
  19994. stdDeviation: 1
  19995. }, {
  19996. tagName: 'feOffset',
  19997. dx: 1,
  19998. dy: 1
  19999. }, {
  20000. tagName: 'feComponentTransfer',
  20001. children: [{
  20002. tagName: 'feFuncA',
  20003. type: 'linear',
  20004. slope: 0.3
  20005. }]
  20006. }, {
  20007. tagName: 'feMerge',
  20008. children: [{
  20009. tagName: 'feMergeNode'
  20010. }, {
  20011. tagName: 'feMergeNode',
  20012. 'in': 'SourceGraphic'
  20013. }]
  20014. }]
  20015. });
  20016. chart.renderer.definition({
  20017. tagName: 'style',
  20018. textContent: '.highcharts-tooltip-' + chart.index + '{' +
  20019. 'filter:url(#drop-shadow-' + chart.index + ')' +
  20020. '}'
  20021. });
  20022. },
  20023. /**
  20024. * Creates the Tooltip label element if it does not exist, then returns it.
  20025. *
  20026. * @function Highcharts.Tooltip#getLabel
  20027. * @return {Highcharts.SVGElement}
  20028. */
  20029. getLabel: function () {
  20030. var tooltip = this, renderer = this.chart.renderer, styledMode = this.chart.styledMode, options = this.options, className = 'tooltip' +
  20031. (defined(options.className) ? ' ' + options.className : ''), container, set;
  20032. if (!this.label) {
  20033. if (this.outside) {
  20034. /**
  20035. * Reference to the tooltip's container, when
  20036. * [Highcharts.Tooltip#outside] is set to true, otherwise
  20037. * it's undefined.
  20038. *
  20039. * @name Highcharts.Tooltip#container
  20040. * @type {Highcharts.HTMLDOMElement|undefined}
  20041. */
  20042. this.container = container = H.doc.createElement('div');
  20043. container.className = 'highcharts-tooltip-container';
  20044. H.css(container, {
  20045. position: 'absolute',
  20046. top: '1px',
  20047. pointerEvents: options.style && options.style.pointerEvents,
  20048. zIndex: 3
  20049. });
  20050. H.doc.body.appendChild(container);
  20051. /**
  20052. * Reference to the tooltip's renderer, when
  20053. * [Highcharts.Tooltip#outside] is set to true, otherwise
  20054. * it's undefined.
  20055. *
  20056. * @name Highcharts.Tooltip#renderer
  20057. * @type {Highcharts.SVGRenderer|undefined}
  20058. */
  20059. this.renderer = renderer = new H.Renderer(container, 0, 0, {}, void 0, void 0, renderer.styledMode);
  20060. }
  20061. // Create the label
  20062. if (this.split) {
  20063. this.label = renderer.g(className);
  20064. }
  20065. else {
  20066. this.label = renderer
  20067. .label('', 0, 0, options.shape || 'callout', null, null, options.useHTML, null, className)
  20068. .attr({
  20069. padding: options.padding,
  20070. r: options.borderRadius
  20071. });
  20072. if (!styledMode) {
  20073. this.label
  20074. .attr({
  20075. fill: options.backgroundColor,
  20076. 'stroke-width': options.borderWidth
  20077. })
  20078. // #2301, #2657
  20079. .css(options.style)
  20080. .shadow(options.shadow);
  20081. }
  20082. }
  20083. if (styledMode) {
  20084. // Apply the drop-shadow filter
  20085. this.applyFilter();
  20086. this.label.addClass('highcharts-tooltip-' + this.chart.index);
  20087. }
  20088. // Split tooltip use updateTooltipContainer to position the tooltip
  20089. // container.
  20090. if (tooltip.outside && !tooltip.split) {
  20091. set = {
  20092. x: this.label.xSetter,
  20093. y: this.label.ySetter
  20094. };
  20095. this.label.xSetter = function (value, key) {
  20096. set[key].call(this.label, tooltip.distance);
  20097. container.style.left = value + 'px';
  20098. };
  20099. this.label.ySetter = function (value, key) {
  20100. set[key].call(this.label, tooltip.distance);
  20101. container.style.top = value + 'px';
  20102. };
  20103. }
  20104. this.label
  20105. .attr({
  20106. zIndex: 8
  20107. })
  20108. .add();
  20109. }
  20110. return this.label;
  20111. },
  20112. /**
  20113. * Updates the tooltip with the provided tooltip options.
  20114. *
  20115. * @function Highcharts.Tooltip#update
  20116. *
  20117. * @param {Highcharts.TooltipOptions} options
  20118. * The tooltip options to update.
  20119. *
  20120. * @return {void}
  20121. */
  20122. update: function (options) {
  20123. this.destroy();
  20124. // Update user options (#6218)
  20125. merge(true, this.chart.options.tooltip.userOptions, options);
  20126. this.init(this.chart, merge(true, this.options, options));
  20127. },
  20128. /**
  20129. * Removes and destroys the tooltip and its elements.
  20130. *
  20131. * @function Highcharts.Tooltip#destroy
  20132. * @return {void}
  20133. */
  20134. destroy: function () {
  20135. // Destroy and clear local variables
  20136. if (this.label) {
  20137. this.label = this.label.destroy();
  20138. }
  20139. if (this.split && this.tt) {
  20140. this.cleanSplit(this.chart, true);
  20141. this.tt = this.tt.destroy();
  20142. }
  20143. if (this.renderer) {
  20144. this.renderer = this.renderer.destroy();
  20145. discardElement(this.container);
  20146. }
  20147. H.clearTimeout(this.hideTimer);
  20148. H.clearTimeout(this.tooltipTimeout);
  20149. },
  20150. /**
  20151. * Moves the tooltip with a soft animation to a new position.
  20152. *
  20153. * @private
  20154. * @function Highcharts.Tooltip#move
  20155. *
  20156. * @param {number} x
  20157. *
  20158. * @param {number} y
  20159. *
  20160. * @param {number} anchorX
  20161. *
  20162. * @param {number} anchorY
  20163. *
  20164. * @return {void}
  20165. */
  20166. move: function (x, y, anchorX, anchorY) {
  20167. var tooltip = this, now = tooltip.now, animate = tooltip.options.animation !== false &&
  20168. !tooltip.isHidden &&
  20169. // When we get close to the target position, abort animation and
  20170. // land on the right place (#3056)
  20171. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1), skipAnchor = tooltip.followPointer || tooltip.len > 1;
  20172. // Get intermediate values for animation
  20173. extend(now, {
  20174. x: animate ? (2 * now.x + x) / 3 : x,
  20175. y: animate ? (now.y + y) / 2 : y,
  20176. anchorX: skipAnchor ?
  20177. void 0 :
  20178. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  20179. anchorY: skipAnchor ?
  20180. void 0 :
  20181. animate ? (now.anchorY + anchorY) / 2 : anchorY
  20182. });
  20183. // Move to the intermediate value
  20184. tooltip.getLabel().attr(now);
  20185. // Run on next tick of the mouse tracker
  20186. if (animate) {
  20187. // Never allow two timeouts
  20188. H.clearTimeout(this.tooltipTimeout);
  20189. // Set the fixed interval ticking for the smooth tooltip
  20190. this.tooltipTimeout = setTimeout(function () {
  20191. // The interval function may still be running during destroy,
  20192. // so check that the chart is really there before calling.
  20193. if (tooltip) {
  20194. tooltip.move(x, y, anchorX, anchorY);
  20195. }
  20196. }, 32);
  20197. }
  20198. },
  20199. /**
  20200. * Hides the tooltip with a fade out animation.
  20201. *
  20202. * @function Highcharts.Tooltip#hide
  20203. *
  20204. * @param {number} [delay]
  20205. * The fade out in milliseconds. If no value is provided the value
  20206. * of the tooltip.hideDelay option is used. A value of 0 disables
  20207. * the fade out animation.
  20208. *
  20209. * @return {void}
  20210. */
  20211. hide: function (delay) {
  20212. var tooltip = this;
  20213. // disallow duplicate timers (#1728, #1766)
  20214. H.clearTimeout(this.hideTimer);
  20215. delay = pick(delay, this.options.hideDelay, 500);
  20216. if (!this.isHidden) {
  20217. this.hideTimer = syncTimeout(function () {
  20218. tooltip.getLabel()[delay ? 'fadeOut' : 'hide']();
  20219. tooltip.isHidden = true;
  20220. }, delay);
  20221. }
  20222. },
  20223. /**
  20224. * Extendable method to get the anchor position of the tooltip
  20225. * from a point or set of points
  20226. *
  20227. * @private
  20228. * @function Highcharts.Tooltip#getAnchor
  20229. *
  20230. * @param {Array<Highcharts.Point>} points
  20231. *
  20232. * @param {Highcharts.PointerEventObject} [mouseEvent]
  20233. *
  20234. * @return {Array<number>}
  20235. */
  20236. getAnchor: function (points, mouseEvent) {
  20237. var ret, chart = this.chart, pointer = chart.pointer, inverted = chart.inverted, plotTop = chart.plotTop, plotLeft = chart.plotLeft, plotX = 0, plotY = 0, yAxis, xAxis;
  20238. points = splat(points);
  20239. // When tooltip follows mouse, relate the position to the mouse
  20240. if (this.followPointer && mouseEvent) {
  20241. if (typeof mouseEvent.chartX === 'undefined') {
  20242. mouseEvent = pointer.normalize(mouseEvent);
  20243. }
  20244. ret = [
  20245. mouseEvent.chartX - chart.plotLeft,
  20246. mouseEvent.chartY - plotTop
  20247. ];
  20248. // Pie uses a special tooltipPos
  20249. }
  20250. else if (points[0].tooltipPos) {
  20251. ret = points[0].tooltipPos;
  20252. // When shared, use the average position
  20253. }
  20254. else {
  20255. points.forEach(function (point) {
  20256. yAxis = point.series.yAxis;
  20257. xAxis = point.series.xAxis;
  20258. plotX += point.plotX +
  20259. (!inverted && xAxis ? xAxis.left - plotLeft : 0);
  20260. plotY += (point.plotLow ?
  20261. (point.plotLow + point.plotHigh) / 2 :
  20262. point.plotY) + (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
  20263. });
  20264. plotX /= points.length;
  20265. plotY /= points.length;
  20266. ret = [
  20267. inverted ? chart.plotWidth - plotY : plotX,
  20268. this.shared && !inverted && points.length > 1 && mouseEvent ?
  20269. // place shared tooltip next to the mouse (#424)
  20270. mouseEvent.chartY - plotTop :
  20271. inverted ? chart.plotHeight - plotX : plotY
  20272. ];
  20273. }
  20274. return ret.map(Math.round);
  20275. },
  20276. /**
  20277. * Place the tooltip in a chart without spilling over
  20278. * and not covering the point it self.
  20279. *
  20280. * @private
  20281. * @function Highcharts.Tooltip#getPosition
  20282. *
  20283. * @param {number} boxWidth
  20284. *
  20285. * @param {number} boxHeight
  20286. *
  20287. * @param {Highcharts.Point} point
  20288. *
  20289. * @return {Highcharts.Dictionary<number>}
  20290. */
  20291. getPosition: function (boxWidth, boxHeight, point) {
  20292. var chart = this.chart, distance = this.distance, ret = {},
  20293. // Don't use h if chart isn't inverted (#7242) ???
  20294. h = (chart.inverted && point.h) || 0, // #4117 ???
  20295. swapped, outside = this.outside, outerWidth = outside ?
  20296. // substract distance to prevent scrollbars
  20297. doc.documentElement.clientWidth - 2 * distance :
  20298. chart.chartWidth, outerHeight = outside ?
  20299. Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight, doc.body.offsetHeight, doc.documentElement.offsetHeight, doc.documentElement.clientHeight) :
  20300. chart.chartHeight, chartPosition = chart.pointer.getChartPosition(), containerScaling = chart.containerScaling, scaleX = function (val) { return ( // eslint-disable-line no-confusing-arrow
  20301. containerScaling ? val * containerScaling.scaleX : val); }, scaleY = function (val) { return ( // eslint-disable-line no-confusing-arrow
  20302. containerScaling ? val * containerScaling.scaleY : val); },
  20303. // Build parameter arrays for firstDimension()/secondDimension()
  20304. buildDimensionArray = function (dim) {
  20305. var isX = dim === 'x';
  20306. return [
  20307. dim,
  20308. isX ? outerWidth : outerHeight,
  20309. isX ? boxWidth : boxHeight
  20310. ].concat(outside ? [
  20311. // If we are using tooltip.outside, we need to scale the
  20312. // position to match scaling of the container in case there
  20313. // is a transform/zoom on the container. #11329
  20314. isX ? scaleX(boxWidth) : scaleY(boxHeight),
  20315. isX ? chartPosition.left - distance +
  20316. scaleX(point.plotX + chart.plotLeft) :
  20317. chartPosition.top - distance +
  20318. scaleY(point.plotY + chart.plotTop),
  20319. 0,
  20320. isX ? outerWidth : outerHeight
  20321. ] : [
  20322. // Not outside, no scaling is needed
  20323. isX ? boxWidth : boxHeight,
  20324. isX ? point.plotX + chart.plotLeft :
  20325. point.plotY + chart.plotTop,
  20326. isX ? chart.plotLeft : chart.plotTop,
  20327. isX ? chart.plotLeft + chart.plotWidth :
  20328. chart.plotTop + chart.plotHeight
  20329. ]);
  20330. }, first = buildDimensionArray('y'), second = buildDimensionArray('x'),
  20331. // The far side is right or bottom
  20332. preferFarSide = !this.followPointer && pick(point.ttBelow, !chart.inverted === !!point.negative), // #4984
  20333. /*
  20334. * Handle the preferred dimension. When the preferred dimension is
  20335. * tooltip on top or bottom of the point, it will look for space
  20336. * there.
  20337. *
  20338. * @private
  20339. */
  20340. firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  20341. point, min, max) {
  20342. var scaledDist = dim === 'y' ?
  20343. 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;
  20344. if (preferFarSide && roomRight) {
  20345. ret[dim] = alignedRight;
  20346. }
  20347. else if (!preferFarSide && roomLeft) {
  20348. ret[dim] = alignedLeft;
  20349. }
  20350. else if (roomLeft) {
  20351. ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
  20352. }
  20353. else if (roomRight) {
  20354. ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
  20355. alignedRight :
  20356. alignedRight + h);
  20357. }
  20358. else {
  20359. return false;
  20360. }
  20361. },
  20362. /*
  20363. * Handle the secondary dimension. If the preferred dimension is
  20364. * tooltip on top or bottom of the point, the second dimension is to
  20365. * align the tooltip above the point, trying to align center but
  20366. * allowing left or right align within the chart box.
  20367. *
  20368. * @private
  20369. */
  20370. secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  20371. point) {
  20372. var retVal;
  20373. // Too close to the edge, return false and swap dimensions
  20374. if (point < distance || point > outerSize - distance) {
  20375. retVal = false;
  20376. // Align left/top
  20377. }
  20378. else if (point < innerSize / 2) {
  20379. ret[dim] = 1;
  20380. // Align right/bottom
  20381. }
  20382. else if (point > outerSize - scaledInnerSize / 2) {
  20383. ret[dim] = outerSize - scaledInnerSize - 2;
  20384. // Align center
  20385. }
  20386. else {
  20387. ret[dim] = point - innerSize / 2;
  20388. }
  20389. return retVal;
  20390. },
  20391. /*
  20392. * Swap the dimensions
  20393. */
  20394. swap = function (count) {
  20395. var temp = first;
  20396. first = second;
  20397. second = temp;
  20398. swapped = count;
  20399. }, run = function () {
  20400. if (firstDimension.apply(0, first) !== false) {
  20401. if (secondDimension.apply(0, second) === false &&
  20402. !swapped) {
  20403. swap(true);
  20404. run();
  20405. }
  20406. }
  20407. else if (!swapped) {
  20408. swap(true);
  20409. run();
  20410. }
  20411. else {
  20412. ret.x = ret.y = 0;
  20413. }
  20414. };
  20415. // Under these conditions, prefer the tooltip on the side of the point
  20416. if (chart.inverted || this.len > 1) {
  20417. swap();
  20418. }
  20419. run();
  20420. return ret;
  20421. },
  20422. /**
  20423. * In case no user defined formatter is given, this will be used. Note that
  20424. * the context here is an object holding point, series, x, y etc.
  20425. *
  20426. * @function Highcharts.Tooltip#defaultFormatter
  20427. *
  20428. * @param {Highcharts.Tooltip} tooltip
  20429. *
  20430. * @return {Array<string>}
  20431. */
  20432. defaultFormatter: function (tooltip) {
  20433. var items = this.points || splat(this), s;
  20434. // Build the header
  20435. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  20436. // build the values
  20437. s = s.concat(tooltip.bodyFormatter(items));
  20438. // footer
  20439. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  20440. return s;
  20441. },
  20442. /**
  20443. * Refresh the tooltip's text and position.
  20444. *
  20445. * @function Highcharts.Tooltip#refresh
  20446. *
  20447. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  20448. * Either a point or an array of points.
  20449. *
  20450. * @param {Highcharts.PointerEventObject} [mouseEvent]
  20451. * Mouse event, that is responsible for the refresh and should be
  20452. * used for the tooltip update.
  20453. *
  20454. * @return {void}
  20455. */
  20456. refresh: function (pointOrPoints, mouseEvent) {
  20457. 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;
  20458. if (!options.enabled) {
  20459. return;
  20460. }
  20461. H.clearTimeout(this.hideTimer);
  20462. // get the reference point coordinates (pie charts use tooltipPos)
  20463. tooltip.followPointer = splat(point)[0].series.tooltipOptions
  20464. .followPointer;
  20465. anchor = tooltip.getAnchor(point, mouseEvent);
  20466. x = anchor[0];
  20467. y = anchor[1];
  20468. // shared tooltip, array is sent over
  20469. if (shared &&
  20470. !(point.series &&
  20471. point.series.noSharedTooltip)) {
  20472. chart.pointer.applyInactiveState(point);
  20473. // Now set hover state for the choosen ones:
  20474. point.forEach(function (item) {
  20475. item.setState('hover');
  20476. pointConfig.push(item.getLabelConfig());
  20477. });
  20478. textConfig = {
  20479. x: point[0].category,
  20480. y: point[0].y
  20481. };
  20482. textConfig.points = pointConfig;
  20483. point = point[0];
  20484. // single point tooltip
  20485. }
  20486. else {
  20487. textConfig = point.getLabelConfig();
  20488. }
  20489. this.len = pointConfig.length; // #6128
  20490. text = formatter.call(textConfig, tooltip);
  20491. // register the current series
  20492. currentSeries = point.series;
  20493. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  20494. // update the inner HTML
  20495. if (text === false) {
  20496. this.hide();
  20497. }
  20498. else {
  20499. // update text
  20500. if (tooltip.split) {
  20501. this.renderSplit(text, splat(pointOrPoints));
  20502. }
  20503. else {
  20504. var label = tooltip.getLabel();
  20505. // Prevent the tooltip from flowing over the chart box (#6659)
  20506. if (!options.style.width || styledMode) {
  20507. label.css({
  20508. width: this.chart.spacingBox.width
  20509. });
  20510. }
  20511. label.attr({
  20512. text: text && text.join ?
  20513. text.join('') :
  20514. text
  20515. });
  20516. // Set the stroke color of the box to reflect the point
  20517. label.removeClass(/highcharts-color-[\d]+/g)
  20518. .addClass('highcharts-color-' +
  20519. pick(point.colorIndex, currentSeries.colorIndex));
  20520. if (!styledMode) {
  20521. label.attr({
  20522. stroke: (options.borderColor ||
  20523. point.color ||
  20524. currentSeries.color ||
  20525. '#666666')
  20526. });
  20527. }
  20528. tooltip.updatePosition({
  20529. plotX: x,
  20530. plotY: y,
  20531. negative: point.negative,
  20532. ttBelow: point.ttBelow,
  20533. h: anchor[2] || 0
  20534. });
  20535. }
  20536. // show it
  20537. if (tooltip.isHidden && tooltip.label) {
  20538. tooltip.label.attr({
  20539. opacity: 1
  20540. }).show();
  20541. }
  20542. tooltip.isHidden = false;
  20543. }
  20544. H.fireEvent(this, 'refresh');
  20545. },
  20546. /**
  20547. * Render the split tooltip. Loops over each point's text and adds
  20548. * a label next to the point, then uses the distribute function to
  20549. * find best non-overlapping positions.
  20550. *
  20551. * @private
  20552. * @function Highcharts.Tooltip#renderSplit
  20553. *
  20554. * @param {string|Array<(boolean|string)>} labels
  20555. *
  20556. * @param {Array<Highcharts.Point>} points
  20557. */
  20558. renderSplit: function (labels, points) {
  20559. var tooltip = this;
  20560. var chart = tooltip.chart, _a = tooltip.chart, chartWidth = _a.chartWidth, chartHeight = _a.chartHeight, plotHeight = _a.plotHeight, plotLeft = _a.plotLeft, plotTop = _a.plotTop, plotWidth = _a.plotWidth, pointer = _a.pointer, ren = _a.renderer, _b = _a.scrollablePixelsX, scrollablePixelsX = _b === void 0 ? 0 : _b, _c = _a.scrollablePixelsY, scrollablePixelsY = _c === void 0 ? 0 : _c, _d = _a.scrollingContainer, _e = _d === void 0 ? { scrollLeft: 0, scrollTop: 0 } : _d, scrollLeft = _e.scrollLeft, scrollTop = _e.scrollTop, styledMode = _a.styledMode, distance = tooltip.distance, options = tooltip.options, positioner = tooltip.options.positioner;
  20561. // The area which the tooltip should be limited to. Limit to scrollable
  20562. // plot area if enabled, otherwise limit to the chart container.
  20563. var boundaries = {
  20564. left: scrollablePixelsX ? plotLeft : 0,
  20565. right: scrollablePixelsX ?
  20566. plotLeft + plotWidth - scrollablePixelsX : chartWidth,
  20567. top: scrollablePixelsY ? plotTop : 0,
  20568. bottom: scrollablePixelsY ?
  20569. plotTop + plotHeight - scrollablePixelsY : chartHeight
  20570. };
  20571. var tooltipLabel = tooltip.getLabel();
  20572. var headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
  20573. var distributionBoxTop = plotTop;
  20574. var headerHeight = 0;
  20575. var maxLength = plotHeight - scrollablePixelsY;
  20576. /**
  20577. * Calculates the anchor position for the partial tooltip
  20578. *
  20579. * @private
  20580. * @param {Highcharts.Point} point The point related to the tooltip
  20581. * @return {object} Returns an object with anchorX and anchorY
  20582. */
  20583. function getAnchor(point) {
  20584. 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;
  20585. var anchorX;
  20586. var anchorY;
  20587. if (isHeader) {
  20588. // Set anchorX to plotX
  20589. anchorX = plotLeft + plotX - scrollLeft;
  20590. // Set anchorY to center of visible plot area.
  20591. anchorY = plotTop + (plotHeight - scrollablePixelsY) / 2;
  20592. }
  20593. else {
  20594. var xAxis = series.xAxis, yAxis = series.yAxis;
  20595. // Set anchorX to plotX. Limit to within xAxis.
  20596. anchorX = xAxis.pos +
  20597. clamp(plotX, -distance, xAxis.len + distance) - scrollLeft;
  20598. // Set anchorY to plotY. Limit to within yAxis.
  20599. anchorY = yAxis.pos + clamp(plotY, 0, yAxis.len) - scrollTop;
  20600. }
  20601. // Limit values to plot area
  20602. anchorX = clamp(anchorX, boundaries.left - distance, boundaries.right + distance);
  20603. anchorY = clamp(anchorY, boundaries.top, boundaries.bottom);
  20604. return { anchorX: anchorX, anchorY: anchorY };
  20605. }
  20606. /**
  20607. * Calculates the position of the partial tooltip
  20608. *
  20609. * @private
  20610. * @param {number} anchorX The partial tooltip anchor x position
  20611. * @param {number} anchorY The partial tooltip anchor y position
  20612. * @param {boolean} isHeader Wether the partial tooltip is a header
  20613. * @param {number} boxWidth Width of the partial tooltip
  20614. * @return {Highcharts.PositionObject} Returns the partial tooltip x and
  20615. * y position
  20616. */
  20617. function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft) {
  20618. if (alignedLeft === void 0) { alignedLeft = true; }
  20619. var y;
  20620. var x;
  20621. if (isHeader) {
  20622. y = headerTop ? 0 : maxLength;
  20623. x = clamp(anchorX - (boxWidth / 2), boundaries.left, boundaries.right - boxWidth);
  20624. }
  20625. else {
  20626. y = anchorY - distributionBoxTop;
  20627. x = alignedLeft ?
  20628. anchorX - boxWidth - distance :
  20629. anchorX + distance;
  20630. x = clamp(x, alignedLeft ? x : boundaries.left, boundaries.right);
  20631. }
  20632. // NOTE: y is relative to distributionBoxTop
  20633. return { x: x, y: y };
  20634. }
  20635. /**
  20636. * Updates the attributes and styling of the partial tooltip. Creates a
  20637. * new partial tooltip if it does not exists.
  20638. *
  20639. * @private
  20640. * @param {Highcharts.SVGElement|undefined} partialTooltip
  20641. * The partial tooltip to update
  20642. * @param {Highcharts.Point} point
  20643. * The point related to the partial tooltip
  20644. * @param {boolean|string} str The text for the partial tooltip
  20645. * @return {Highcharts.SVGElement} Returns the updated partial tooltip
  20646. */
  20647. function updatePartialTooltip(partialTooltip, point, str) {
  20648. var tt = partialTooltip;
  20649. var isHeader = point.isHeader, series = point.series;
  20650. var colorClass = 'highcharts-color-' + pick(point.colorIndex, series.colorIndex, 'none');
  20651. if (!tt) {
  20652. var attribs = {
  20653. padding: options.padding,
  20654. r: options.borderRadius
  20655. };
  20656. if (!styledMode) {
  20657. attribs.fill = options.backgroundColor;
  20658. attribs['stroke-width'] = options.borderWidth;
  20659. }
  20660. tt = ren
  20661. .label(null, null, null, (options[isHeader ? 'headerShape' : 'shape']) ||
  20662. 'callout', null, null, options.useHTML)
  20663. .addClass(isHeader ? 'highcharts-tooltip-header ' : '' +
  20664. 'highcharts-tooltip-box ' +
  20665. colorClass)
  20666. .attr(attribs)
  20667. .add(tooltipLabel);
  20668. }
  20669. tt.isActive = true;
  20670. tt.attr({
  20671. text: str
  20672. });
  20673. if (!styledMode) {
  20674. tt.css(options.style)
  20675. .shadow(options.shadow)
  20676. .attr({
  20677. stroke: (options.borderColor ||
  20678. point.color ||
  20679. series.color ||
  20680. '#333333')
  20681. });
  20682. }
  20683. return tt;
  20684. }
  20685. // Graceful degradation for legacy formatters
  20686. if (isString(labels)) {
  20687. labels = [false, labels];
  20688. }
  20689. // Create the individual labels for header and points, ignore footer
  20690. var boxes = labels.slice(0, points.length + 1).reduce(function (boxes, str, i) {
  20691. if (str !== false && str !== '') {
  20692. var point = points[i - 1] || {
  20693. // Item 0 is the header. Instead of this, we could also
  20694. // use the crosshair label
  20695. isHeader: true,
  20696. plotX: points[0].plotX,
  20697. plotY: plotHeight,
  20698. series: {}
  20699. };
  20700. var isHeader = point.isHeader;
  20701. // Store the tooltip label referance on the series
  20702. var owner = isHeader ? tooltip : point.series;
  20703. var tt = owner.tt = updatePartialTooltip(owner.tt, point, str);
  20704. // Get X position now, so we can move all to the other side in
  20705. // case of overflow
  20706. var bBox = tt.getBBox();
  20707. var boxWidth = bBox.width + tt.strokeWidth();
  20708. if (isHeader) {
  20709. headerHeight = bBox.height;
  20710. maxLength += headerHeight;
  20711. if (headerTop) {
  20712. distributionBoxTop -= headerHeight;
  20713. }
  20714. }
  20715. var _a = getAnchor(point), anchorX = _a.anchorX, anchorY = _a.anchorY;
  20716. var size = bBox.height + 1;
  20717. var boxPosition = positioner ? positioner.call(tooltip, boxWidth, size, point) : defaultPositioner(anchorX, anchorY, isHeader, boxWidth);
  20718. boxes.push({
  20719. // 0-align to the top, 1-align to the bottom
  20720. align: positioner ? 0 : void 0,
  20721. anchorX: anchorX,
  20722. anchorY: anchorY,
  20723. boxWidth: boxWidth,
  20724. point: point,
  20725. rank: pick(boxPosition.rank, isHeader ? 1 : 0),
  20726. size: size,
  20727. target: boxPosition.y,
  20728. tt: tt,
  20729. x: boxPosition.x
  20730. });
  20731. }
  20732. return boxes;
  20733. }, []);
  20734. // If overflow left then align all labels to the right
  20735. if (!positioner && boxes.some(function (box) { return box.x < 0; })) {
  20736. boxes = boxes.map(function (box) {
  20737. var _a = defaultPositioner(box.anchorX, box.anchorY, box.point.isHeader, box.boxWidth, false), x = _a.x, y = _a.y;
  20738. return extend(box, {
  20739. target: y,
  20740. x: x
  20741. });
  20742. });
  20743. }
  20744. // Clean previous run (for missing points)
  20745. tooltip.cleanSplit();
  20746. // Distribute and put in place
  20747. H.distribute(boxes, maxLength, void 0);
  20748. boxes.forEach(function (box) {
  20749. var anchorX = box.anchorX, anchorY = box.anchorY, pos = box.pos, x = box.x;
  20750. // Put the label in place
  20751. box.tt.attr({
  20752. visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
  20753. x: x,
  20754. /* NOTE: y should equal pos to be consistent with !split
  20755. * tooltip, but is currently relative to plotTop. Is left as is
  20756. * to avoid breaking change. Remove distributionBoxTop to make
  20757. * it consistent.
  20758. */
  20759. y: pos + distributionBoxTop,
  20760. anchorX: anchorX,
  20761. anchorY: anchorY
  20762. });
  20763. });
  20764. /* If we have a seperate tooltip container, then update the necessary
  20765. * container properties.
  20766. * Test that tooltip has its own container and renderer before executing
  20767. * the operation.
  20768. */
  20769. var container = tooltip.container, outside = tooltip.outside, renderer = tooltip.renderer;
  20770. if (outside && container && renderer) {
  20771. // Set container size to fit the tooltip
  20772. var _f = tooltipLabel.getBBox(), width = _f.width, height = _f.height, x = _f.x, y = _f.y;
  20773. renderer.setSize(width + x, height + y, false);
  20774. // Position the tooltip container to the chart container
  20775. var chartPosition = pointer.getChartPosition();
  20776. container.style.left = chartPosition.left + 'px';
  20777. container.style.top = chartPosition.top + 'px';
  20778. }
  20779. },
  20780. /**
  20781. * Find the new position and perform the move
  20782. *
  20783. * @private
  20784. * @function Highcharts.Tooltip#updatePosition
  20785. *
  20786. * @param {Highcharts.Point} point
  20787. */
  20788. updatePosition: function (point) {
  20789. var chart = this.chart, pointer = chart.pointer, label = this.getLabel(), pos, anchorX = point.plotX + chart.plotLeft, anchorY = point.plotY + chart.plotTop, pad;
  20790. // Needed for outside: true (#11688)
  20791. var chartPosition = pointer.getChartPosition();
  20792. pos = (this.options.positioner || this.getPosition).call(this, label.width, label.height, point);
  20793. // Set the renderer size dynamically to prevent document size to change
  20794. if (this.outside) {
  20795. pad = (this.options.borderWidth || 0) + 2 * this.distance;
  20796. this.renderer.setSize(label.width + pad, label.height + pad, false);
  20797. // Anchor and tooltip container need scaling if chart container has
  20798. // scale transform/css zoom. #11329.
  20799. var containerScaling = chart.containerScaling;
  20800. if (containerScaling) {
  20801. H.css(this.container, {
  20802. transform: "scale(" + containerScaling.scaleX + ", " + containerScaling.scaleY + ")"
  20803. });
  20804. anchorX *= containerScaling.scaleX;
  20805. anchorY *= containerScaling.scaleY;
  20806. }
  20807. anchorX += chartPosition.left - pos.x;
  20808. anchorY += chartPosition.top - pos.y;
  20809. }
  20810. // do the move
  20811. this.move(Math.round(pos.x), Math.round(pos.y || 0), // can be undefined (#3977)
  20812. anchorX, anchorY);
  20813. },
  20814. /**
  20815. * Get the optimal date format for a point, based on a range.
  20816. *
  20817. * @private
  20818. * @function Highcharts.Tooltip#getDateFormat
  20819. *
  20820. * @param {number} range
  20821. * The time range
  20822. *
  20823. * @param {number} date
  20824. * The date of the point in question
  20825. *
  20826. * @param {number} startOfWeek
  20827. * An integer representing the first day of the week, where 0 is
  20828. * Sunday.
  20829. *
  20830. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  20831. * A map of time units to formats.
  20832. *
  20833. * @return {string}
  20834. * The optimal date format for a point.
  20835. */
  20836. getDateFormat: function (range, date, startOfWeek, dateTimeLabelFormats) {
  20837. 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 = {
  20838. millisecond: 15,
  20839. second: 12,
  20840. minute: 9,
  20841. hour: 6,
  20842. day: 3
  20843. }, lastN = 'millisecond'; // for sub-millisecond data, #4223
  20844. for (n in timeUnits) { // eslint-disable-line guard-for-in
  20845. // If the range is exactly one week and we're looking at a
  20846. // Sunday/Monday, go for the week format
  20847. if (range === timeUnits.week &&
  20848. +time.dateFormat('%w', date) === startOfWeek &&
  20849. dateStr.substr(6) === blank.substr(6)) {
  20850. n = 'week';
  20851. break;
  20852. }
  20853. // The first format that is too great for the range
  20854. if (timeUnits[n] > range) {
  20855. n = lastN;
  20856. break;
  20857. }
  20858. // If the point is placed every day at 23:59, we need to show
  20859. // the minutes as well. #2637.
  20860. if (strpos[n] &&
  20861. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
  20862. break;
  20863. }
  20864. // Weeks are outside the hierarchy, only apply them on
  20865. // Mondays/Sundays like in the first condition
  20866. if (n !== 'week') {
  20867. lastN = n;
  20868. }
  20869. }
  20870. if (n) {
  20871. format = time.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  20872. }
  20873. return format;
  20874. },
  20875. /**
  20876. * Get the best X date format based on the closest point range on the axis.
  20877. *
  20878. * @private
  20879. * @function Highcharts.Tooltip#getXDateFormat
  20880. *
  20881. * @param {Highcharts.Point} point
  20882. *
  20883. * @param {Highcharts.TooltipOptions} options
  20884. *
  20885. * @param {Highcharts.Axis} xAxis
  20886. *
  20887. * @return {string}
  20888. */
  20889. getXDateFormat: function (point, options, xAxis) {
  20890. var xDateFormat, dateTimeLabelFormats = options.dateTimeLabelFormats, closestPointRange = xAxis && xAxis.closestPointRange;
  20891. if (closestPointRange) {
  20892. xDateFormat = this.getDateFormat(closestPointRange, point.x, xAxis.options.startOfWeek, dateTimeLabelFormats);
  20893. }
  20894. else {
  20895. xDateFormat = dateTimeLabelFormats.day;
  20896. }
  20897. return xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
  20898. },
  20899. /**
  20900. * Format the footer/header of the tooltip
  20901. * #3397: abstraction to enable formatting of footer and header
  20902. *
  20903. * @private
  20904. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  20905. * @param {Highcharts.PointLabelObject} labelConfig
  20906. * @param {boolean} [isFooter]
  20907. * @return {string}
  20908. */
  20909. tooltipFooterHeaderFormatter: function (labelConfig, isFooter) {
  20910. var footOrHead = isFooter ? 'footer' : 'header', series = labelConfig.series, tooltipOptions = series.tooltipOptions, xDateFormat = tooltipOptions.xDateFormat, xAxis = series.xAxis, isDateTime = (xAxis &&
  20911. xAxis.options.type === 'datetime' &&
  20912. isNumber(labelConfig.key)), formatString = tooltipOptions[footOrHead + 'Format'], evt = {
  20913. isFooter: isFooter,
  20914. labelConfig: labelConfig
  20915. };
  20916. H.fireEvent(this, 'headerFormatter', evt, function (e) {
  20917. // Guess the best date format based on the closest point distance
  20918. // (#568, #3418)
  20919. if (isDateTime && !xDateFormat) {
  20920. xDateFormat = this.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  20921. }
  20922. // Insert the footer date format if any
  20923. if (isDateTime && xDateFormat) {
  20924. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  20925. ['key']).forEach(function (key) {
  20926. formatString = formatString.replace('{point.' + key + '}', '{point.' + key + ':' + xDateFormat + '}');
  20927. });
  20928. }
  20929. // Replace default header style with class name
  20930. if (series.chart.styledMode) {
  20931. formatString = this.styledModeFormat(formatString);
  20932. }
  20933. e.text = format(formatString, {
  20934. point: labelConfig,
  20935. series: series
  20936. }, this.chart);
  20937. });
  20938. return evt.text;
  20939. },
  20940. /**
  20941. * Build the body (lines) of the tooltip by iterating over the items and
  20942. * returning one entry for each item, abstracting this functionality allows
  20943. * to easily overwrite and extend it.
  20944. *
  20945. * @private
  20946. * @function Highcharts.Tooltip#bodyFormatter
  20947. * @param {Array<(Highcharts.Point|Highcharts.Series)>} items
  20948. * @return {Array<string>}
  20949. */
  20950. bodyFormatter: function (items) {
  20951. return items.map(function (item) {
  20952. var tooltipOptions = item.series.tooltipOptions;
  20953. return (tooltipOptions[(item.point.formatPrefix || 'point') + 'Formatter'] ||
  20954. item.point.tooltipFormatter).call(item.point, tooltipOptions[(item.point.formatPrefix || 'point') + 'Format'] || '');
  20955. });
  20956. },
  20957. styledModeFormat: function (formatString) {
  20958. return formatString
  20959. .replace('style="font-size: 10px"', 'class="highcharts-header"')
  20960. .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex}"');
  20961. }
  20962. };
  20963. });
  20964. _registerModule(_modules, 'parts/Pointer.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  20965. /* *
  20966. *
  20967. * (c) 2010-2019 Torstein Honsi
  20968. *
  20969. * License: www.highcharts.com/license
  20970. *
  20971. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  20972. *
  20973. * */
  20974. /**
  20975. * One position in relation to an axis.
  20976. *
  20977. * @interface Highcharts.PointerAxisCoordinateObject
  20978. */ /**
  20979. * Related axis.
  20980. *
  20981. * @name Highcharts.PointerAxisCoordinateObject#axis
  20982. * @type {Highcharts.Axis}
  20983. */ /**
  20984. * Axis value.
  20985. *
  20986. * @name Highcharts.PointerAxisCoordinateObject#value
  20987. * @type {number}
  20988. */
  20989. /**
  20990. * Positions in terms of axis values.
  20991. *
  20992. * @interface Highcharts.PointerAxisCoordinatesObject
  20993. */ /**
  20994. * Positions on the x-axis.
  20995. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  20996. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  20997. */ /**
  20998. * Positions on the y-axis.
  20999. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  21000. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  21001. */
  21002. /**
  21003. * Pointer coordinates.
  21004. *
  21005. * @interface Highcharts.PointerCoordinatesObject
  21006. */ /**
  21007. * @name Highcharts.PointerCoordinatesObject#chartX
  21008. * @type {number}
  21009. */ /**
  21010. * @name Highcharts.PointerCoordinatesObject#chartY
  21011. * @type {number}
  21012. */
  21013. /**
  21014. * A native browser mouse or touch event, extended with position information
  21015. * relative to the {@link Chart.container}.
  21016. *
  21017. * @interface Highcharts.PointerEventObject
  21018. * @extends global.PointerEvent
  21019. */ /**
  21020. * The X coordinate of the pointer interaction relative to the chart.
  21021. *
  21022. * @name Highcharts.PointerEventObject#chartX
  21023. * @type {number}
  21024. */ /**
  21025. * The Y coordinate of the pointer interaction relative to the chart.
  21026. *
  21027. * @name Highcharts.PointerEventObject#chartY
  21028. * @type {number}
  21029. */
  21030. /**
  21031. * Axis-specific data of a selection.
  21032. *
  21033. * @interface Highcharts.SelectDataObject
  21034. */ /**
  21035. * @name Highcharts.SelectDataObject#axis
  21036. * @type {Highcharts.Axis}
  21037. */ /**
  21038. * @name Highcharts.SelectDataObject#max
  21039. * @type {number}
  21040. */ /**
  21041. * @name Highcharts.SelectDataObject#min
  21042. * @type {number}
  21043. */
  21044. /**
  21045. * Object for select events.
  21046. *
  21047. * @interface Highcharts.SelectEventObject
  21048. */ /**
  21049. * @name Highcharts.SelectEventObject#originalEvent
  21050. * @type {global.Event}
  21051. */ /**
  21052. * @name Highcharts.SelectEventObject#xAxis
  21053. * @type {Array<Highcharts.SelectDataObject>}
  21054. */ /**
  21055. * @name Highcharts.SelectEventObject#yAxis
  21056. * @type {Array<Highcharts.SelectDataObject>}
  21057. */
  21058. var attr = U.attr, defined = U.defined, extend = U.extend, isNumber = U.isNumber, isObject = U.isObject, objectEach = U.objectEach, offset = U.offset, pick = U.pick, splat = U.splat;
  21059. var H = Highcharts, addEvent = H.addEvent, charts = H.charts, color = H.color, css = H.css, find = H.find, fireEvent = H.fireEvent, Tooltip = H.Tooltip;
  21060. /* eslint-disable no-invalid-this, valid-jsdoc */
  21061. /**
  21062. * The mouse and touch tracker object. Each {@link Chart} item has one
  21063. * assosiated Pointer item that can be accessed from the {@link Chart.pointer}
  21064. * property.
  21065. *
  21066. * @class
  21067. * @name Highcharts.Pointer
  21068. *
  21069. * @param {Highcharts.Chart} chart
  21070. * The Chart instance.
  21071. *
  21072. * @param {Highcharts.Options} options
  21073. * The root options object. The pointer uses options from the chart and
  21074. * tooltip structures.
  21075. */
  21076. Highcharts.Pointer = function (chart, options) {
  21077. this.init(chart, options);
  21078. };
  21079. Highcharts.Pointer.prototype = {
  21080. /**
  21081. * Initialize the Pointer.
  21082. *
  21083. * @private
  21084. * @function Highcharts.Pointer#init
  21085. *
  21086. * @param {Highcharts.Chart} chart
  21087. * The Chart instance.
  21088. *
  21089. * @param {Highcharts.Options} options
  21090. * The root options object. The pointer uses options from the chart
  21091. * and tooltip structures.
  21092. *
  21093. * @return {void}
  21094. */
  21095. init: function (chart, options) {
  21096. // Store references
  21097. this.options = options;
  21098. this.chart = chart;
  21099. // Do we need to handle click on a touch device?
  21100. this.runChartClick =
  21101. options.chart.events &&
  21102. !!options.chart.events.click;
  21103. this.pinchDown = [];
  21104. this.lastValidTouch = {};
  21105. if (Tooltip) {
  21106. /**
  21107. * Tooltip object for points of series.
  21108. *
  21109. * @name Highcharts.Chart#tooltip
  21110. * @type {Highcharts.Tooltip}
  21111. */
  21112. chart.tooltip = new Tooltip(chart, options.tooltip);
  21113. this.followTouchMove = pick(options.tooltip.followTouchMove, true);
  21114. }
  21115. this.setDOMEvents();
  21116. },
  21117. /**
  21118. * Resolve the zoomType option, this is reset on all touch start and mouse
  21119. * down events.
  21120. *
  21121. * @private
  21122. * @function Highcharts.Pointer#zoomOption
  21123. *
  21124. * @param {global.Event} e
  21125. * Event object.
  21126. *
  21127. * @param {void}
  21128. */
  21129. zoomOption: function (e) {
  21130. var chart = this.chart, options = chart.options.chart, zoomType = options.zoomType || '', inverted = chart.inverted, zoomX, zoomY;
  21131. // Look for the pinchType option
  21132. if (/touch/.test(e.type)) {
  21133. zoomType = pick(options.pinchType, zoomType);
  21134. }
  21135. this.zoomX = zoomX = /x/.test(zoomType);
  21136. this.zoomY = zoomY = /y/.test(zoomType);
  21137. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  21138. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  21139. this.hasZoom = zoomX || zoomY;
  21140. },
  21141. /**
  21142. * Return the cached chartPosition if it is available on the Pointer,
  21143. * otherwise find it. Running offset is quite expensive, so it should be
  21144. * avoided when we know the chart hasn't moved.
  21145. *
  21146. * @function Highcharts.Pointer#getChartPosition
  21147. *
  21148. * @return {Highcharts.OffsetObject}
  21149. * The offset of the chart container within the page
  21150. */
  21151. getChartPosition: function () {
  21152. var chart = this.chart;
  21153. var container = chart.scrollingContainer || chart.container;
  21154. return (this.chartPosition ||
  21155. (this.chartPosition = offset(container)));
  21156. },
  21157. /**
  21158. * Takes a browser event object and extends it with custom Highcharts
  21159. * properties `chartX` and `chartY` in order to work on the internal
  21160. * coordinate system.
  21161. *
  21162. * @function Highcharts.Pointer#normalize
  21163. *
  21164. * @param {PointerEvent|TouchEvent} e
  21165. * Event object in standard browsers.
  21166. *
  21167. * @param {Highcharts.OffsetObject} [chartPosition]
  21168. * Additional chart offset.
  21169. *
  21170. * @return {Highcharts.PointerEventObject}
  21171. * A browser event with extended properties `chartX` and `chartY`.
  21172. */
  21173. normalize: function (e, chartPosition) {
  21174. var ePos;
  21175. // iOS (#2757)
  21176. ePos = e.touches ?
  21177. (e.touches.length ?
  21178. e.touches.item(0) :
  21179. e.changedTouches[0]) :
  21180. e;
  21181. // Get mouse position
  21182. if (!chartPosition) {
  21183. chartPosition = this.getChartPosition();
  21184. }
  21185. var chartX = ePos.pageX - chartPosition.left, chartY = ePos.pageY - chartPosition.top;
  21186. // #11329 - when there is scaling on a parent element, we need to take
  21187. // this into account
  21188. var containerScaling = this.chart.containerScaling;
  21189. if (containerScaling) {
  21190. chartX /= containerScaling.scaleX;
  21191. chartY /= containerScaling.scaleY;
  21192. }
  21193. return extend(e, {
  21194. chartX: Math.round(chartX),
  21195. chartY: Math.round(chartY)
  21196. });
  21197. },
  21198. /**
  21199. * Get the click position in terms of axis values.
  21200. *
  21201. * @function Highcharts.Pointer#getCoordinates
  21202. *
  21203. * @param {Highcharts.PointerEventObject} e
  21204. * Pointer event, extended with `chartX` and `chartY` properties.
  21205. *
  21206. * @return {Highcharts.PointerAxisCoordinatesObject}
  21207. */
  21208. getCoordinates: function (e) {
  21209. var coordinates = {
  21210. xAxis: [],
  21211. yAxis: []
  21212. };
  21213. this.chart.axes.forEach(function (axis) {
  21214. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  21215. axis: axis,
  21216. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  21217. });
  21218. });
  21219. return coordinates;
  21220. },
  21221. /**
  21222. * Finds the closest point to a set of coordinates, using the k-d-tree
  21223. * algorithm.
  21224. *
  21225. * @function Highcharts.Pointer#findNearestKDPoints
  21226. *
  21227. * @param {Array<Highcharts.Series>} series
  21228. * All the series to search in.
  21229. *
  21230. * @param {boolean|undefined} shared
  21231. * Whether it is a shared tooltip or not.
  21232. *
  21233. * @param {Highcharts.PointerEventObject} e
  21234. * The pointer event object, containing chart coordinates of the
  21235. * pointer.
  21236. *
  21237. * @return {Highcharts.Point|undefined}
  21238. * The point closest to given coordinates.
  21239. */
  21240. findNearestKDPoint: function (series, shared, e) {
  21241. var closest, sort = function (p1, p2) {
  21242. var isCloserX = p1.distX - p2.distX, isCloser = p1.dist - p2.dist, isAbove = (p2.series.group && p2.series.group.zIndex) -
  21243. (p1.series.group && p1.series.group.zIndex), result;
  21244. // We have two points which are not in the same place on xAxis
  21245. // and shared tooltip:
  21246. if (isCloserX !== 0 && shared) { // #5721
  21247. result = isCloserX;
  21248. // Points are not exactly in the same place on x/yAxis:
  21249. }
  21250. else if (isCloser !== 0) {
  21251. result = isCloser;
  21252. // The same xAxis and yAxis position, sort by z-index:
  21253. }
  21254. else if (isAbove !== 0) {
  21255. result = isAbove;
  21256. // The same zIndex, sort by array index:
  21257. }
  21258. else {
  21259. result =
  21260. p1.series.index > p2.series.index ?
  21261. -1 :
  21262. 1;
  21263. }
  21264. return result;
  21265. };
  21266. series.forEach(function (s) {
  21267. var noSharedTooltip = s.noSharedTooltip && shared, compareX = (!noSharedTooltip &&
  21268. s.options.findNearestPointBy.indexOf('y') < 0), point = s.searchPoint(e, compareX);
  21269. if ( // Check that we actually found a point on the series.
  21270. isObject(point, true) &&
  21271. // Use the new point if it is closer.
  21272. (!isObject(closest, true) ||
  21273. (sort(closest, point) > 0))) {
  21274. closest = point;
  21275. }
  21276. });
  21277. return closest;
  21278. },
  21279. /**
  21280. * @private
  21281. * @function Highcharts.Pointer#getPointFromEvent
  21282. *
  21283. * @param {global.Event} e
  21284. *
  21285. * @return {Highcharts.Point|undefined}
  21286. */
  21287. getPointFromEvent: function (e) {
  21288. var target = e.target, point;
  21289. while (target && !point) {
  21290. point = target.point;
  21291. target = target.parentNode;
  21292. }
  21293. return point;
  21294. },
  21295. /**
  21296. * @private
  21297. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  21298. * @param {Highcharts.Point} point
  21299. * @param {boolean} [inverted]
  21300. * @return {Highcharts.PointerCoordinatesObject|undefined}
  21301. */
  21302. getChartCoordinatesFromPoint: function (point, inverted) {
  21303. var series = point.series, xAxis = series.xAxis, yAxis = series.yAxis, plotX = pick(point.clientX, point.plotX), shapeArgs = point.shapeArgs;
  21304. if (xAxis && yAxis) {
  21305. return inverted ? {
  21306. chartX: xAxis.len + xAxis.pos - plotX,
  21307. chartY: yAxis.len + yAxis.pos - point.plotY
  21308. } : {
  21309. chartX: plotX + xAxis.pos,
  21310. chartY: point.plotY + yAxis.pos
  21311. };
  21312. }
  21313. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  21314. // E.g. pies do not have axes
  21315. return {
  21316. chartX: shapeArgs.x,
  21317. chartY: shapeArgs.y
  21318. };
  21319. }
  21320. },
  21321. /**
  21322. * Calculates what is the current hovered point/points and series.
  21323. *
  21324. * @private
  21325. * @function Highcharts.Pointer#getHoverData
  21326. *
  21327. * @param {Highcharts.Point|undefined} existingHoverPoint
  21328. * The point currrently beeing hovered.
  21329. *
  21330. * @param {Highcharts.Series|undefined} existingHoverSeries
  21331. * The series currently beeing hovered.
  21332. *
  21333. * @param {Array<Highcharts.Series>} series
  21334. * All the series in the chart.
  21335. *
  21336. * @param {boolean} isDirectTouch
  21337. * Is the pointer directly hovering the point.
  21338. *
  21339. * @param {boolean|undefined} shared
  21340. * Whether it is a shared tooltip or not.
  21341. *
  21342. * @param {Highcharts.PointerEventObject} [e]
  21343. * The triggering event, containing chart coordinates of the pointer.
  21344. *
  21345. * @return {object}
  21346. * Object containing resulting hover data: hoverPoint, hoverSeries,
  21347. * and hoverPoints.
  21348. */
  21349. getHoverData: function (existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  21350. var hoverPoint, hoverPoints = [], hoverSeries = existingHoverSeries, useExisting = !!(isDirectTouch && existingHoverPoint), notSticky = hoverSeries && !hoverSeries.stickyTracking, filter = function (s) {
  21351. return (s.visible &&
  21352. !(!shared && s.directTouch) && // #3821
  21353. pick(s.options.enableMouseTracking, true));
  21354. },
  21355. // Which series to look in for the hover point
  21356. searchSeries = notSticky ?
  21357. // Only search on hovered series if it has stickyTracking false
  21358. [hoverSeries] :
  21359. // Filter what series to look in.
  21360. series.filter(function (s) {
  21361. return filter(s) && s.stickyTracking;
  21362. });
  21363. // Use existing hovered point or find the one closest to coordinates.
  21364. hoverPoint = useExisting || !e ?
  21365. existingHoverPoint :
  21366. this.findNearestKDPoint(searchSeries, shared, e);
  21367. // Assign hover series
  21368. hoverSeries = hoverPoint && hoverPoint.series;
  21369. // If we have a hoverPoint, assign hoverPoints.
  21370. if (hoverPoint) {
  21371. // When tooltip is shared, it displays more than one point
  21372. if (shared && !hoverSeries.noSharedTooltip) {
  21373. searchSeries = series.filter(function (s) {
  21374. return filter(s) && !s.noSharedTooltip;
  21375. });
  21376. // Get all points with the same x value as the hoverPoint
  21377. searchSeries.forEach(function (s) {
  21378. var point = find(s.points, function (p) {
  21379. return p.x === hoverPoint.x && !p.isNull;
  21380. });
  21381. if (isObject(point)) {
  21382. /*
  21383. * Boost returns a minimal point. Convert it to a usable
  21384. * point for tooltip and states.
  21385. */
  21386. if (s.chart.isBoosting) {
  21387. point = s.getPoint(point);
  21388. }
  21389. hoverPoints.push(point);
  21390. }
  21391. });
  21392. }
  21393. else {
  21394. hoverPoints.push(hoverPoint);
  21395. }
  21396. }
  21397. return {
  21398. hoverPoint: hoverPoint,
  21399. hoverSeries: hoverSeries,
  21400. hoverPoints: hoverPoints
  21401. };
  21402. },
  21403. /**
  21404. * With line type charts with a single tracker, get the point closest to the
  21405. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  21406. *
  21407. * @private
  21408. * @function Highcharts.Pointer#runPointActions
  21409. *
  21410. * @param {global.Event} e
  21411. *
  21412. * @param {Highcharts.PointerEventObject} [p]
  21413. *
  21414. * @return {void}
  21415. *
  21416. * @fires Highcharts.Point#event:mouseOut
  21417. * @fires Highcharts.Point#event:mouseOver
  21418. */
  21419. runPointActions: function (e, p) {
  21420. var pointer = this, chart = pointer.chart, series = chart.series, tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  21421. chart.tooltip :
  21422. void 0), shared = (tooltip ?
  21423. tooltip.shared :
  21424. false), hoverPoint = p || chart.hoverPoint, hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
  21425. // onMouseOver or already hovering a series with directTouch
  21426. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  21427. pointer.isDirectTouch)), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e), useSharedTooltip, followPointer, anchor, points;
  21428. // Update variables from hoverData.
  21429. hoverPoint = hoverData.hoverPoint;
  21430. points = hoverData.hoverPoints;
  21431. hoverSeries = hoverData.hoverSeries;
  21432. followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
  21433. useSharedTooltip = (shared &&
  21434. hoverSeries &&
  21435. !hoverSeries.noSharedTooltip);
  21436. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  21437. // #3926, #4200
  21438. if (hoverPoint &&
  21439. // !(hoverSeries && hoverSeries.directTouch) &&
  21440. (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))) {
  21441. (chart.hoverPoints || []).forEach(function (p) {
  21442. if (points.indexOf(p) === -1) {
  21443. p.setState();
  21444. }
  21445. });
  21446. // Set normal state to previous series
  21447. if (chart.hoverSeries !== hoverSeries) {
  21448. hoverSeries.onMouseOver();
  21449. }
  21450. pointer.applyInactiveState(points);
  21451. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  21452. (points || []).forEach(function (p) {
  21453. p.setState('hover');
  21454. });
  21455. // If tracking is on series in stead of on each point,
  21456. // fire mouseOver on hover point. // #4448
  21457. if (chart.hoverPoint) {
  21458. chart.hoverPoint.firePointEvent('mouseOut');
  21459. }
  21460. // Hover point may have been destroyed in the event handlers (#7127)
  21461. if (!hoverPoint.series) {
  21462. return;
  21463. }
  21464. hoverPoint.firePointEvent('mouseOver');
  21465. /**
  21466. * Contains all hovered points.
  21467. *
  21468. * @name Highcharts.Chart#hoverPoints
  21469. * @type {Array<Highcharts.Point>|null}
  21470. */
  21471. chart.hoverPoints = points;
  21472. /**
  21473. * Contains the original hovered point.
  21474. *
  21475. * @name Highcharts.Chart#hoverPoint
  21476. * @type {Highcharts.Point|null}
  21477. */
  21478. chart.hoverPoint = hoverPoint;
  21479. // Draw tooltip if necessary
  21480. if (tooltip) {
  21481. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  21482. }
  21483. // Update positions (regardless of kdpoint or hoverPoint)
  21484. }
  21485. else if (followPointer && tooltip && !tooltip.isHidden) {
  21486. anchor = tooltip.getAnchor([{}], e);
  21487. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  21488. }
  21489. // Start the event listener to pick up the tooltip and crosshairs
  21490. if (!pointer.unDocMouseMove) {
  21491. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  21492. var chart = charts[H.hoverChartIndex];
  21493. if (chart) {
  21494. chart.pointer.onDocumentMouseMove(e);
  21495. }
  21496. });
  21497. }
  21498. // Issues related to crosshair #4927, #5269 #5066, #5658
  21499. chart.axes.forEach(function drawAxisCrosshair(axis) {
  21500. var snap = pick(axis.crosshair.snap, true), point = !snap ?
  21501. void 0 :
  21502. H.find(points, function (p) {
  21503. return p.series[axis.coll] === axis;
  21504. });
  21505. // Axis has snapping crosshairs, and one of the hover points belongs
  21506. // to axis. Always call drawCrosshair when it is not snap.
  21507. if (point || !snap) {
  21508. axis.drawCrosshair(e, point);
  21509. // Axis has snapping crosshairs, but no hover point belongs to axis
  21510. }
  21511. else {
  21512. axis.hideCrosshair();
  21513. }
  21514. });
  21515. },
  21516. /**
  21517. * Set inactive state to all series that are not currently hovered,
  21518. * or, if `inactiveOtherPoints` is set to true, set inactive state to
  21519. * all points within that series.
  21520. *
  21521. * @function Highcharts.Pointer#applyInactiveState
  21522. *
  21523. * @private
  21524. *
  21525. * @param {Array<Highcharts.Point>} points
  21526. * Currently hovered points
  21527. *
  21528. */
  21529. applyInactiveState: function (points) {
  21530. var activeSeries = [], series;
  21531. // Get all active series from the hovered points
  21532. (points || []).forEach(function (item) {
  21533. series = item.series;
  21534. // Include itself
  21535. activeSeries.push(series);
  21536. // Include parent series
  21537. if (series.linkedParent) {
  21538. activeSeries.push(series.linkedParent);
  21539. }
  21540. // Include all child series
  21541. if (series.linkedSeries) {
  21542. activeSeries = activeSeries.concat(series.linkedSeries);
  21543. }
  21544. // Include navigator series
  21545. if (series.navigatorSeries) {
  21546. activeSeries.push(series.navigatorSeries);
  21547. }
  21548. });
  21549. // Now loop over all series, filtering out active series
  21550. this.chart.series.forEach(function (inactiveSeries) {
  21551. if (activeSeries.indexOf(inactiveSeries) === -1) {
  21552. // Inactive series
  21553. inactiveSeries.setState('inactive', true);
  21554. }
  21555. else if (inactiveSeries.options.inactiveOtherPoints) {
  21556. // Active series, but other points should be inactivated
  21557. inactiveSeries.setAllPointsToState('inactive');
  21558. }
  21559. });
  21560. },
  21561. /**
  21562. * Reset the tracking by hiding the tooltip, the hover series state and the
  21563. * hover point
  21564. *
  21565. * @function Highcharts.Pointer#reset
  21566. *
  21567. * @param {boolean} [allowMove]
  21568. * Instead of destroying the tooltip altogether, allow moving it if
  21569. * possible.
  21570. *
  21571. * @param {number} [delay]
  21572. *
  21573. * @return {void}
  21574. */
  21575. reset: function (allowMove, delay) {
  21576. var pointer = this, chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, hoverPoints = chart.hoverPoints, tooltip = chart.tooltip, tooltipPoints = tooltip && tooltip.shared ?
  21577. hoverPoints :
  21578. hoverPoint;
  21579. // Check if the points have moved outside the plot area (#1003, #4736,
  21580. // #5101)
  21581. if (allowMove && tooltipPoints) {
  21582. splat(tooltipPoints).forEach(function (point) {
  21583. if (point.series.isCartesian &&
  21584. typeof point.plotX === 'undefined') {
  21585. allowMove = false;
  21586. }
  21587. });
  21588. }
  21589. // Just move the tooltip, #349
  21590. if (allowMove) {
  21591. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  21592. tooltip.refresh(tooltipPoints);
  21593. if (tooltip.shared && hoverPoints) { // #8284
  21594. hoverPoints.forEach(function (point) {
  21595. point.setState(point.state, true);
  21596. if (point.series.isCartesian) {
  21597. if (point.series.xAxis.crosshair) {
  21598. point.series.xAxis
  21599. .drawCrosshair(null, point);
  21600. }
  21601. if (point.series.yAxis.crosshair) {
  21602. point.series.yAxis
  21603. .drawCrosshair(null, point);
  21604. }
  21605. }
  21606. });
  21607. }
  21608. else if (hoverPoint) { // #2500
  21609. hoverPoint.setState(hoverPoint.state, true);
  21610. chart.axes.forEach(function (axis) {
  21611. if (axis.crosshair &&
  21612. hoverPoint.series[axis.coll] === axis) {
  21613. axis.drawCrosshair(null, hoverPoint);
  21614. }
  21615. });
  21616. }
  21617. }
  21618. // Full reset
  21619. }
  21620. else {
  21621. if (hoverPoint) {
  21622. hoverPoint.onMouseOut();
  21623. }
  21624. if (hoverPoints) {
  21625. hoverPoints.forEach(function (point) {
  21626. point.setState();
  21627. });
  21628. }
  21629. if (hoverSeries) {
  21630. hoverSeries.onMouseOut();
  21631. }
  21632. if (tooltip) {
  21633. tooltip.hide(delay);
  21634. }
  21635. if (pointer.unDocMouseMove) {
  21636. pointer.unDocMouseMove = pointer.unDocMouseMove();
  21637. }
  21638. // Remove crosshairs
  21639. chart.axes.forEach(function (axis) {
  21640. axis.hideCrosshair();
  21641. });
  21642. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  21643. }
  21644. },
  21645. /**
  21646. * Scale series groups to a certain scale and translation.
  21647. *
  21648. * @private
  21649. * @function Highcharts.Pointer#scaleGroups
  21650. *
  21651. * @param {Highcharts.SeriesPlotBoxObject} [attribs]
  21652. *
  21653. * @param {boolean} [clip]
  21654. *
  21655. * @return {void}
  21656. */
  21657. scaleGroups: function (attribs, clip) {
  21658. var chart = this.chart, seriesAttribs;
  21659. // Scale each series
  21660. chart.series.forEach(function (series) {
  21661. seriesAttribs = attribs || series.getPlotBox(); // #1701
  21662. if (series.xAxis && series.xAxis.zoomEnabled && series.group) {
  21663. series.group.attr(seriesAttribs);
  21664. if (series.markerGroup) {
  21665. series.markerGroup.attr(seriesAttribs);
  21666. series.markerGroup.clip(clip ? chart.clipRect : null);
  21667. }
  21668. if (series.dataLabelsGroup) {
  21669. series.dataLabelsGroup.attr(seriesAttribs);
  21670. }
  21671. }
  21672. });
  21673. // Clip
  21674. chart.clipRect.attr(clip || chart.clipBox);
  21675. },
  21676. /**
  21677. * Start a drag operation.
  21678. *
  21679. * @private
  21680. * @function Highcharts.Pointer#dragStart
  21681. *
  21682. * @param {Highcharts.PointerEventObject} e
  21683. *
  21684. * @return {void}
  21685. */
  21686. dragStart: function (e) {
  21687. var chart = this.chart;
  21688. // Record the start position
  21689. chart.mouseIsDown = e.type;
  21690. chart.cancelClick = false;
  21691. chart.mouseDownX = this.mouseDownX = e.chartX;
  21692. chart.mouseDownY = this.mouseDownY = e.chartY;
  21693. },
  21694. /**
  21695. * Perform a drag operation in response to a mousemove event while the mouse
  21696. * is down.
  21697. *
  21698. * @private
  21699. * @function Highcharts.Pointer#drag
  21700. *
  21701. * @param {Highcharts.PointerEventObject} e
  21702. *
  21703. * @return {void}
  21704. */
  21705. drag: function (e) {
  21706. 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, mouseDownY = this.mouseDownY, panningEnabled = isObject(chartOptions.panning) ?
  21707. chartOptions.panning && chartOptions.panning.enabled :
  21708. chartOptions.panning, panKey = (chartOptions.panKey && e[chartOptions.panKey + 'Key']);
  21709. // If the device supports both touch and mouse (like IE11), and we are
  21710. // touch-dragging inside the plot area, don't handle the mouse event.
  21711. // #4339.
  21712. if (selectionMarker && selectionMarker.touch) {
  21713. return;
  21714. }
  21715. // If the mouse is outside the plot area, adjust to cooordinates
  21716. // inside to prevent the selection marker from going outside
  21717. if (chartX < plotLeft) {
  21718. chartX = plotLeft;
  21719. }
  21720. else if (chartX > plotLeft + plotWidth) {
  21721. chartX = plotLeft + plotWidth;
  21722. }
  21723. if (chartY < plotTop) {
  21724. chartY = plotTop;
  21725. }
  21726. else if (chartY > plotTop + plotHeight) {
  21727. chartY = plotTop + plotHeight;
  21728. }
  21729. // determine if the mouse has moved more than 10px
  21730. this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
  21731. Math.pow(mouseDownY - chartY, 2));
  21732. if (this.hasDragged > 10) {
  21733. clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
  21734. // make a selection
  21735. if (chart.hasCartesianSeries &&
  21736. (this.zoomX || this.zoomY) &&
  21737. clickedInside &&
  21738. !panKey) {
  21739. if (!selectionMarker) {
  21740. this.selectionMarker = selectionMarker =
  21741. chart.renderer.rect(plotLeft, plotTop, zoomHor ? 1 : plotWidth, zoomVert ? 1 : plotHeight, 0)
  21742. .attr({
  21743. 'class': 'highcharts-selection-marker',
  21744. zIndex: 7
  21745. })
  21746. .add();
  21747. if (!chart.styledMode) {
  21748. selectionMarker.attr({
  21749. fill: (chartOptions.selectionMarkerFill ||
  21750. color('#335cad')
  21751. .setOpacity(0.25).get())
  21752. });
  21753. }
  21754. }
  21755. }
  21756. // adjust the width of the selection marker
  21757. if (selectionMarker && zoomHor) {
  21758. size = chartX - mouseDownX;
  21759. selectionMarker.attr({
  21760. width: Math.abs(size),
  21761. x: (size > 0 ? 0 : size) + mouseDownX
  21762. });
  21763. }
  21764. // adjust the height of the selection marker
  21765. if (selectionMarker && zoomVert) {
  21766. size = chartY - mouseDownY;
  21767. selectionMarker.attr({
  21768. height: Math.abs(size),
  21769. y: (size > 0 ? 0 : size) + mouseDownY
  21770. });
  21771. }
  21772. // panning
  21773. if (clickedInside &&
  21774. !selectionMarker &&
  21775. panningEnabled) {
  21776. chart.pan(e, chartOptions.panning);
  21777. }
  21778. }
  21779. },
  21780. /**
  21781. * On mouse up or touch end across the entire document, drop the selection.
  21782. *
  21783. * @private
  21784. * @function Highcharts.Pointer#drop
  21785. *
  21786. * @param {global.Event} e
  21787. *
  21788. * @return {void}
  21789. */
  21790. drop: function (e) {
  21791. var pointer = this, chart = this.chart, hasPinched = this.hasPinched;
  21792. if (this.selectionMarker) {
  21793. var selectionData = {
  21794. originalEvent: e,
  21795. xAxis: [],
  21796. yAxis: []
  21797. }, selectionBox = this.selectionMarker, selectionLeft = selectionBox.attr ?
  21798. selectionBox.attr('x') :
  21799. selectionBox.x, selectionTop = selectionBox.attr ?
  21800. selectionBox.attr('y') :
  21801. selectionBox.y, selectionWidth = selectionBox.attr ?
  21802. selectionBox.attr('width') :
  21803. selectionBox.width, selectionHeight = selectionBox.attr ?
  21804. selectionBox.attr('height') :
  21805. selectionBox.height, runZoom;
  21806. // a selection has been made
  21807. if (this.hasDragged || hasPinched) {
  21808. // record each axis' min and max
  21809. chart.axes.forEach(function (axis) {
  21810. if (axis.zoomEnabled &&
  21811. defined(axis.min) &&
  21812. (hasPinched ||
  21813. pointer[{
  21814. xAxis: 'zoomX',
  21815. yAxis: 'zoomY'
  21816. }[axis.coll]])) { // #859, #3569
  21817. var horiz = axis.horiz, minPixelPadding = e.type === 'touchend' ?
  21818. axis.minPixelPadding :
  21819. 0, // #1207, #3075
  21820. selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop) +
  21821. minPixelPadding), selectionMax = axis.toValue((horiz ?
  21822. selectionLeft + selectionWidth :
  21823. selectionTop + selectionHeight) - minPixelPadding);
  21824. selectionData[axis.coll].push({
  21825. axis: axis,
  21826. // Min/max for reversed axes
  21827. min: Math.min(selectionMin, selectionMax),
  21828. max: Math.max(selectionMin, selectionMax)
  21829. });
  21830. runZoom = true;
  21831. }
  21832. });
  21833. if (runZoom) {
  21834. fireEvent(chart, 'selection', selectionData, function (args) {
  21835. chart.zoom(extend(args, hasPinched ?
  21836. { animation: false } :
  21837. null));
  21838. });
  21839. }
  21840. }
  21841. if (isNumber(chart.index)) {
  21842. this.selectionMarker = this.selectionMarker.destroy();
  21843. }
  21844. // Reset scaling preview
  21845. if (hasPinched) {
  21846. this.scaleGroups();
  21847. }
  21848. }
  21849. // Reset all. Check isNumber because it may be destroyed on mouse up
  21850. // (#877)
  21851. if (chart && isNumber(chart.index)) {
  21852. css(chart.container, { cursor: chart._cursor });
  21853. chart.cancelClick = this.hasDragged > 10; // #370
  21854. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  21855. this.pinchDown = [];
  21856. }
  21857. },
  21858. /**
  21859. * @private
  21860. * @function Highcharts.Pointer#onContainerMouseDown
  21861. *
  21862. * @param {Highcharts.PointerEventObject} e
  21863. *
  21864. * @return {void}
  21865. */
  21866. onContainerMouseDown: function (e) {
  21867. // Normalize before the 'if' for the legacy IE (#7850)
  21868. e = this.normalize(e);
  21869. if (e.button !== 2) {
  21870. this.zoomOption(e);
  21871. // issue #295, dragging not always working in Firefox
  21872. if (e.preventDefault) {
  21873. e.preventDefault();
  21874. }
  21875. this.dragStart(e);
  21876. }
  21877. },
  21878. /**
  21879. * @private
  21880. * @function Highcharts.Pointer#onDocumentMouseUp
  21881. *
  21882. * @param {Highcharts.PointerEventObject} e
  21883. *
  21884. * @return {void}
  21885. */
  21886. onDocumentMouseUp: function (e) {
  21887. if (charts[H.hoverChartIndex]) {
  21888. charts[H.hoverChartIndex].pointer.drop(e);
  21889. }
  21890. },
  21891. /**
  21892. * Special handler for mouse move that will hide the tooltip when the mouse
  21893. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  21894. * always fire.
  21895. *
  21896. * @private
  21897. * @function Highcharts.Pointer#onDocumentMouseMove
  21898. *
  21899. * @param {Highcharts.PointerEventObject} e
  21900. *
  21901. * @return {void}
  21902. */
  21903. onDocumentMouseMove: function (e) {
  21904. var chart = this.chart, chartPosition = this.chartPosition;
  21905. e = this.normalize(e, chartPosition);
  21906. // If we're outside, hide the tooltip
  21907. if (chartPosition &&
  21908. !this.inClass(e.target, 'highcharts-tracker') &&
  21909. !chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
  21910. this.reset();
  21911. }
  21912. },
  21913. /**
  21914. * When mouse leaves the container, hide the tooltip.
  21915. *
  21916. * @private
  21917. * @function Highcharts.Pointer#onContainerMouseLeave
  21918. *
  21919. * @param {Highcharts.PointerEventObject} e
  21920. *
  21921. * @return {void}
  21922. */
  21923. onContainerMouseLeave: function (e) {
  21924. var chart = charts[H.hoverChartIndex];
  21925. // #4886, MS Touch end fires mouseleave but with no related target
  21926. if (chart && (e.relatedTarget || e.toElement)) {
  21927. chart.pointer.reset();
  21928. // Also reset the chart position, used in #149 fix
  21929. chart.pointer.chartPosition = void 0;
  21930. }
  21931. },
  21932. /**
  21933. * The mousemove, touchmove and touchstart event handler
  21934. *
  21935. * @private
  21936. * @function Highcharts.Pointer#onContainerMouseMove
  21937. *
  21938. * @param {Highcharts.PointerEventObject} e
  21939. *
  21940. * @return {void}
  21941. */
  21942. onContainerMouseMove: function (e) {
  21943. var chart = this.chart;
  21944. if (!defined(H.hoverChartIndex) ||
  21945. !charts[H.hoverChartIndex] ||
  21946. !charts[H.hoverChartIndex].mouseIsDown) {
  21947. H.hoverChartIndex = chart.index;
  21948. }
  21949. e = this.normalize(e);
  21950. // In IE8 we apparently need this returnValue set to false in order to
  21951. // avoid text being selected. But in Chrome, e.returnValue is prevented,
  21952. // plus we don't need to run e.preventDefault to prevent selected text
  21953. // in modern browsers. So we set it conditionally. Remove it when IE8 is
  21954. // no longer needed. #2251, #3224.
  21955. if (!e.preventDefault) {
  21956. e.returnValue = false;
  21957. }
  21958. if (chart.mouseIsDown === 'mousedown') {
  21959. this.drag(e);
  21960. }
  21961. // Show the tooltip and run mouse over events (#977)
  21962. if ((this.inClass(e.target, 'highcharts-tracker') ||
  21963. chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) &&
  21964. !chart.openMenu) {
  21965. this.runPointActions(e);
  21966. }
  21967. },
  21968. /**
  21969. * Utility to detect whether an element has, or has a parent with, a
  21970. * specificclass name. Used on detection of tracker objects and on deciding
  21971. * whether hovering the tooltip should cause the active series to mouse out.
  21972. *
  21973. * @function Highcharts.Pointer#inClass
  21974. *
  21975. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  21976. * The element to investigate.
  21977. *
  21978. * @param {string} className
  21979. * The class name to look for.
  21980. *
  21981. * @return {boolean|undefined}
  21982. * True if either the element or one of its parents has the given
  21983. * class name.
  21984. */
  21985. inClass: function (element, className) {
  21986. var elemClassName;
  21987. while (element) {
  21988. elemClassName = attr(element, 'class');
  21989. if (elemClassName) {
  21990. if (elemClassName.indexOf(className) !== -1) {
  21991. return true;
  21992. }
  21993. if (elemClassName.indexOf('highcharts-container') !== -1) {
  21994. return false;
  21995. }
  21996. }
  21997. element = element.parentNode;
  21998. }
  21999. },
  22000. /**
  22001. * @private
  22002. * @function Highcharts.Pointer#onTrackerMouseOut
  22003. *
  22004. * @param {Highcharts.PointerEventObject} e
  22005. *
  22006. * @return {void}
  22007. */
  22008. onTrackerMouseOut: function (e) {
  22009. var series = this.chart.hoverSeries, relatedTarget = e.relatedTarget || e.toElement;
  22010. this.isDirectTouch = false;
  22011. if (series &&
  22012. relatedTarget &&
  22013. !series.stickyTracking &&
  22014. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  22015. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  22016. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  22017. series.onMouseOut();
  22018. }
  22019. },
  22020. /**
  22021. * @private
  22022. * @function Highcharts.Pointer#onContainerClick
  22023. *
  22024. * @param {Highcharts.PointerEventObject} e
  22025. *
  22026. * @return {void}
  22027. */
  22028. onContainerClick: function (e) {
  22029. var chart = this.chart, hoverPoint = chart.hoverPoint, plotLeft = chart.plotLeft, plotTop = chart.plotTop;
  22030. e = this.normalize(e);
  22031. if (!chart.cancelClick) {
  22032. // On tracker click, fire the series and point events. #783, #1583
  22033. if (hoverPoint &&
  22034. this.inClass(e.target, 'highcharts-tracker')) {
  22035. // the series click event
  22036. fireEvent(hoverPoint.series, 'click', extend(e, {
  22037. point: hoverPoint
  22038. }));
  22039. // the point click event
  22040. if (chart.hoverPoint) { // it may be destroyed (#1844)
  22041. hoverPoint.firePointEvent('click', e);
  22042. }
  22043. // When clicking outside a tracker, fire a chart event
  22044. }
  22045. else {
  22046. extend(e, this.getCoordinates(e));
  22047. // fire a click event in the chart
  22048. if (chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) {
  22049. fireEvent(chart, 'click', e);
  22050. }
  22051. }
  22052. }
  22053. },
  22054. /**
  22055. * Set the JS DOM events on the container and document. This method should
  22056. * contain a one-to-one assignment between methods and their handlers. Any
  22057. * advanced logic should be moved to the handler reflecting the event's
  22058. * name.
  22059. *
  22060. * @private
  22061. * @function Highcharts.Pointer#setDOMEvents
  22062. *
  22063. * @return {void}
  22064. */
  22065. setDOMEvents: function () {
  22066. var pointer = this, container = pointer.chart.container, ownerDoc = container.ownerDocument;
  22067. container.onmousedown = function (e) {
  22068. pointer.onContainerMouseDown(e);
  22069. };
  22070. container.onmousemove = function (e) {
  22071. pointer.onContainerMouseMove(e);
  22072. };
  22073. container.onclick = function (e) {
  22074. pointer.onContainerClick(e);
  22075. };
  22076. this.unbindContainerMouseLeave = addEvent(container, 'mouseleave', pointer.onContainerMouseLeave);
  22077. if (!H.unbindDocumentMouseUp) {
  22078. H.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', pointer.onDocumentMouseUp);
  22079. }
  22080. if (H.hasTouch) {
  22081. addEvent(container, 'touchstart', function (e) {
  22082. pointer.onContainerTouchStart(e);
  22083. });
  22084. addEvent(container, 'touchmove', function (e) {
  22085. pointer.onContainerTouchMove(e);
  22086. });
  22087. if (!H.unbindDocumentTouchEnd) {
  22088. H.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', pointer.onDocumentTouchEnd);
  22089. }
  22090. }
  22091. },
  22092. /**
  22093. * Destroys the Pointer object and disconnects DOM events.
  22094. *
  22095. * @function Highcharts.Pointer#destroy
  22096. *
  22097. * @return {void}
  22098. */
  22099. destroy: function () {
  22100. var pointer = this;
  22101. if (pointer.unDocMouseMove) {
  22102. pointer.unDocMouseMove();
  22103. }
  22104. this.unbindContainerMouseLeave();
  22105. if (!H.chartCount) {
  22106. if (H.unbindDocumentMouseUp) {
  22107. H.unbindDocumentMouseUp = H.unbindDocumentMouseUp();
  22108. }
  22109. if (H.unbindDocumentTouchEnd) {
  22110. H.unbindDocumentTouchEnd = H.unbindDocumentTouchEnd();
  22111. }
  22112. }
  22113. // memory and CPU leak
  22114. clearInterval(pointer.tooltipTimeout);
  22115. objectEach(pointer, function (val, prop) {
  22116. pointer[prop] = null;
  22117. });
  22118. }
  22119. };
  22120. });
  22121. _registerModule(_modules, 'parts/TouchPointer.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  22122. /* *
  22123. *
  22124. * (c) 2010-2019 Torstein Honsi
  22125. *
  22126. * License: www.highcharts.com/license
  22127. *
  22128. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22129. *
  22130. * */
  22131. var extend = U.extend, pick = U.pick;
  22132. var charts = H.charts, noop = H.noop, Pointer = H.Pointer;
  22133. /* eslint-disable no-invalid-this, valid-jsdoc */
  22134. // Support for touch devices
  22135. extend(Pointer.prototype, /** @lends Pointer.prototype */ {
  22136. /**
  22137. * Run translation operations
  22138. *
  22139. * @private
  22140. * @function Highcharts.Pointer#pinchTranslate
  22141. *
  22142. * @param {Array<*>} pinchDown
  22143. *
  22144. * @param {Array<*>} touches
  22145. *
  22146. * @param {*} transform
  22147. *
  22148. * @param {*} selectionMarker
  22149. *
  22150. * @param {*} clip
  22151. *
  22152. * @param {*} lastValidTouch
  22153. *
  22154. * @return {void}
  22155. */
  22156. pinchTranslate: function (pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  22157. if (this.zoomHor) {
  22158. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  22159. }
  22160. if (this.zoomVert) {
  22161. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  22162. }
  22163. },
  22164. /**
  22165. * Run translation operations for each direction (horizontal and vertical)
  22166. * independently.
  22167. *
  22168. * @private
  22169. * @function Highcharts.Pointer#pinchTranslateDirection
  22170. *
  22171. * @param {boolean} horiz
  22172. *
  22173. * @param {Array<*>} pinchDown
  22174. *
  22175. * @param {Array<*>} touches
  22176. *
  22177. * @param {*} transform
  22178. *
  22179. * @param {*} selectionMarker
  22180. *
  22181. * @param {*} clip
  22182. *
  22183. * @param {*} lastValidTouch
  22184. *
  22185. * @param {number|undefined} [forcedScale=1]
  22186. *
  22187. * @return {void}
  22188. */
  22189. pinchTranslateDirection: function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  22190. 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 () {
  22191. // Don't zoom if fingers are too close on this axis
  22192. if (!singleTouch && Math.abs(touch0Start - touch1Start) > 20) {
  22193. scale = forcedScale ||
  22194. Math.abs(touch0Now - touch1Now) /
  22195. Math.abs(touch0Start - touch1Start);
  22196. }
  22197. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  22198. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  22199. };
  22200. // Set the scale, first pass
  22201. setScale();
  22202. // The clip position (x or y) is altered if out of bounds, the selection
  22203. // position is not
  22204. selectionXY = clipXY;
  22205. // Out of bounds
  22206. if (selectionXY < bounds.min) {
  22207. selectionXY = bounds.min;
  22208. outOfBounds = true;
  22209. }
  22210. else if (selectionXY + selectionWH > bounds.max) {
  22211. selectionXY = bounds.max - selectionWH;
  22212. outOfBounds = true;
  22213. }
  22214. // Is the chart dragged off its bounds, determined by dataMin and
  22215. // dataMax?
  22216. if (outOfBounds) {
  22217. // Modify the touchNow position in order to create an elastic drag
  22218. // movement. This indicates to the user that the chart is responsive
  22219. // but can't be dragged further.
  22220. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  22221. if (!singleTouch) {
  22222. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  22223. }
  22224. // Set the scale, second pass to adapt to the modified touchNow
  22225. // positions
  22226. setScale();
  22227. }
  22228. else {
  22229. lastValidTouch[xy] = [touch0Now, touch1Now];
  22230. }
  22231. // Set geometry for clipping, selection and transformation
  22232. if (!inverted) {
  22233. clip[xy] = clipXY - plotLeftTop;
  22234. clip[wh] = selectionWH;
  22235. }
  22236. scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  22237. transformScale = inverted ? 1 / scale : scale;
  22238. selectionMarker[wh] = selectionWH;
  22239. selectionMarker[xy] = selectionXY;
  22240. transform[scaleKey] = scale;
  22241. transform['translate' + XY] = (transformScale * plotLeftTop) +
  22242. (touch0Now - (transformScale * touch0Start));
  22243. },
  22244. /**
  22245. * Handle touch events with two touches
  22246. *
  22247. * @private
  22248. * @function Highcharts.Pointer#pinch
  22249. *
  22250. * @param {Highcharts.PointerEventObject} e
  22251. *
  22252. * @return {void}
  22253. */
  22254. pinch: function (e) {
  22255. 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') &&
  22256. chart.runTrackerClick) ||
  22257. self.runChartClick), clip = {};
  22258. // Don't initiate panning until the user has pinched. This prevents us
  22259. // from blocking page scrolling as users scroll down a long page
  22260. // (#4210).
  22261. if (touchesLength > 1) {
  22262. self.initiated = true;
  22263. }
  22264. // On touch devices, only proceed to trigger click if a handler is
  22265. // defined
  22266. if (hasZoom && self.initiated && !fireClickEvent) {
  22267. e.preventDefault();
  22268. }
  22269. // Normalize each touch
  22270. [].map.call(touches, function (e) {
  22271. return self.normalize(e);
  22272. });
  22273. // Register the touch start position
  22274. if (e.type === 'touchstart') {
  22275. [].forEach.call(touches, function (e, i) {
  22276. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  22277. });
  22278. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  22279. pinchDown[1].chartX];
  22280. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  22281. pinchDown[1].chartY];
  22282. // Identify the data bounds in pixels
  22283. chart.axes.forEach(function (axis) {
  22284. if (axis.zoomEnabled) {
  22285. 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);
  22286. // Store the bounds for use in the touchmove handler
  22287. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  22288. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  22289. }
  22290. });
  22291. self.res = true; // reset on next move
  22292. // Optionally move the tooltip on touchmove
  22293. }
  22294. else if (self.followTouchMove && touchesLength === 1) {
  22295. this.runPointActions(self.normalize(e));
  22296. // Event type is touchmove, handle panning and pinching
  22297. }
  22298. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  22299. // fires first
  22300. // Set the marker
  22301. if (!selectionMarker) {
  22302. self.selectionMarker = selectionMarker = extend({
  22303. destroy: noop,
  22304. touch: true
  22305. }, chart.plotBox);
  22306. }
  22307. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  22308. self.hasPinched = hasZoom;
  22309. // Scale and translate the groups to provide visual feedback during
  22310. // pinching
  22311. self.scaleGroups(transform, clip);
  22312. if (self.res) {
  22313. self.res = false;
  22314. this.reset(false, 0);
  22315. }
  22316. }
  22317. },
  22318. /**
  22319. * General touch handler shared by touchstart and touchmove.
  22320. *
  22321. * @private
  22322. * @function Highcharts.Pointer#touch
  22323. *
  22324. * @param {Highcharts.PointerEventObject} e
  22325. *
  22326. * @param {boolean} [start]
  22327. *
  22328. * @return {void}
  22329. */
  22330. touch: function (e, start) {
  22331. var chart = this.chart, hasMoved, pinchDown, isInside;
  22332. if (chart.index !== H.hoverChartIndex) {
  22333. this.onContainerMouseLeave({ relatedTarget: true });
  22334. }
  22335. H.hoverChartIndex = chart.index;
  22336. if (e.touches.length === 1) {
  22337. e = this.normalize(e);
  22338. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop);
  22339. if (isInside && !chart.openMenu) {
  22340. // Run mouse events and display tooltip etc
  22341. if (start) {
  22342. this.runPointActions(e);
  22343. }
  22344. // Android fires touchmove events after the touchstart even if
  22345. // the finger hasn't moved, or moved only a pixel or two. In iOS
  22346. // however, the touchmove doesn't fire unless the finger moves
  22347. // more than ~4px. So we emulate this behaviour in Android by
  22348. // checking how much it moved, and cancelling on small
  22349. // distances. #3450.
  22350. if (e.type === 'touchmove') {
  22351. pinchDown = this.pinchDown;
  22352. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  22353. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  22354. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  22355. }
  22356. if (pick(hasMoved, true)) {
  22357. this.pinch(e);
  22358. }
  22359. }
  22360. else if (start) {
  22361. // Hide the tooltip on touching outside the plot area (#1203)
  22362. this.reset();
  22363. }
  22364. }
  22365. else if (e.touches.length === 2) {
  22366. this.pinch(e);
  22367. }
  22368. },
  22369. /**
  22370. * @private
  22371. * @function Highcharts.Pointer#onContainerTouchStart
  22372. *
  22373. * @param {Highcharts.PointerEventObject} e
  22374. *
  22375. * @return {void}
  22376. */
  22377. onContainerTouchStart: function (e) {
  22378. this.zoomOption(e);
  22379. this.touch(e, true);
  22380. },
  22381. /**
  22382. * @private
  22383. * @function Highcharts.Pointer#onContainerTouchMove
  22384. *
  22385. * @param {Highcharts.PointerEventObject} e
  22386. *
  22387. * @return {void}
  22388. */
  22389. onContainerTouchMove: function (e) {
  22390. this.touch(e);
  22391. },
  22392. /**
  22393. * @private
  22394. * @function Highcharts.Pointer#onDocumentTouchEnd
  22395. *
  22396. * @param {Highcharts.PointerEventObject} e
  22397. *
  22398. * @return {void}
  22399. */
  22400. onDocumentTouchEnd: function (e) {
  22401. if (charts[H.hoverChartIndex]) {
  22402. charts[H.hoverChartIndex].pointer.drop(e);
  22403. }
  22404. }
  22405. });
  22406. });
  22407. _registerModule(_modules, 'parts/MSPointer.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  22408. /* *
  22409. *
  22410. * (c) 2010-2019 Torstein Honsi
  22411. *
  22412. * License: www.highcharts.com/license
  22413. *
  22414. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22415. *
  22416. * */
  22417. /* globals MSPointerEvent, PointerEvent */
  22418. var extend = U.extend, objectEach = U.objectEach, wrap = U.wrap;
  22419. var addEvent = H.addEvent, charts = H.charts, css = H.css, doc = H.doc, hasTouch = H.hasTouch, noop = H.noop, Pointer = H.Pointer, removeEvent = H.removeEvent, win = H.win;
  22420. if (!hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
  22421. // The touches object keeps track of the points being touched at all times
  22422. var touches = {}, hasPointerEvent = !!win.PointerEvent, getWebkitTouches = function () {
  22423. var fake = [];
  22424. fake.item = function (i) {
  22425. return this[i];
  22426. };
  22427. objectEach(touches, function (touch) {
  22428. fake.push({
  22429. pageX: touch.pageX,
  22430. pageY: touch.pageY,
  22431. target: touch.target
  22432. });
  22433. });
  22434. return fake;
  22435. }, translateMSPointer = function (e, method, wktype, func) {
  22436. var p;
  22437. if ((e.pointerType === 'touch' ||
  22438. e.pointerType === e.MSPOINTER_TYPE_TOUCH) && charts[H.hoverChartIndex]) {
  22439. func(e);
  22440. p = charts[H.hoverChartIndex].pointer;
  22441. p[method]({
  22442. type: wktype,
  22443. target: e.currentTarget,
  22444. preventDefault: noop,
  22445. touches: getWebkitTouches()
  22446. });
  22447. }
  22448. };
  22449. /* eslint-disable no-invalid-this, valid-jsdoc */
  22450. // Extend the Pointer prototype with methods for each event handler and more
  22451. extend(Pointer.prototype, /** @lends Pointer.prototype */ {
  22452. /**
  22453. * @private
  22454. * @function Highcharts.Pointer#onContainerPointerDown
  22455. *
  22456. * @param {Highcharts.PointerEventObject} e
  22457. *
  22458. * @return {void}
  22459. */
  22460. onContainerPointerDown: function (e) {
  22461. translateMSPointer(e, 'onContainerTouchStart', 'touchstart', function (e) {
  22462. touches[e.pointerId] = {
  22463. pageX: e.pageX,
  22464. pageY: e.pageY,
  22465. target: e.currentTarget
  22466. };
  22467. });
  22468. },
  22469. /**
  22470. * @private
  22471. * @function Highcharts.Pointer#onContainerPointerMove
  22472. *
  22473. * @param {Highcharts.PointerEventObject} e
  22474. *
  22475. * @return {void}
  22476. */
  22477. onContainerPointerMove: function (e) {
  22478. translateMSPointer(e, 'onContainerTouchMove', 'touchmove', function (e) {
  22479. touches[e.pointerId] = ({ pageX: e.pageX, pageY: e.pageY });
  22480. if (!touches[e.pointerId].target) {
  22481. touches[e.pointerId].target = e.currentTarget;
  22482. }
  22483. });
  22484. },
  22485. /**
  22486. * @private
  22487. * @function Highcharts.Pointer#onDocumentPointerUp
  22488. *
  22489. * @param {Highcharts.PointerEventObject} e
  22490. *
  22491. * @return {void}
  22492. */
  22493. onDocumentPointerUp: function (e) {
  22494. translateMSPointer(e, 'onDocumentTouchEnd', 'touchend', function (e) {
  22495. delete touches[e.pointerId];
  22496. });
  22497. },
  22498. /**
  22499. * Add or remove the MS Pointer specific events
  22500. *
  22501. * @private
  22502. * @function Highcharts.Pointer#batchMSEvents
  22503. *
  22504. * @param {Function} fn
  22505. *
  22506. * @return {void}
  22507. */
  22508. batchMSEvents: function (fn) {
  22509. fn(this.chart.container, hasPointerEvent ? 'pointerdown' : 'MSPointerDown', this.onContainerPointerDown);
  22510. fn(this.chart.container, hasPointerEvent ? 'pointermove' : 'MSPointerMove', this.onContainerPointerMove);
  22511. fn(doc, hasPointerEvent ? 'pointerup' : 'MSPointerUp', this.onDocumentPointerUp);
  22512. }
  22513. });
  22514. // Disable default IE actions for pinch and such on chart element
  22515. wrap(Pointer.prototype, 'init', function (proceed, chart, options) {
  22516. proceed.call(this, chart, options);
  22517. if (this.hasZoom) { // #4014
  22518. css(chart.container, {
  22519. '-ms-touch-action': 'none',
  22520. 'touch-action': 'none'
  22521. });
  22522. }
  22523. });
  22524. // Add IE specific touch events to chart
  22525. wrap(Pointer.prototype, 'setDOMEvents', function (proceed) {
  22526. proceed.apply(this);
  22527. if (this.hasZoom || this.followTouchMove) {
  22528. this.batchMSEvents(addEvent);
  22529. }
  22530. });
  22531. // Destroy MS events also
  22532. wrap(Pointer.prototype, 'destroy', function (proceed) {
  22533. this.batchMSEvents(removeEvent);
  22534. proceed.call(this);
  22535. });
  22536. }
  22537. });
  22538. _registerModule(_modules, 'parts/Legend.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  22539. /* *
  22540. *
  22541. * (c) 2010-2019 Torstein Honsi
  22542. *
  22543. * License: www.highcharts.com/license
  22544. *
  22545. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22546. *
  22547. * */
  22548. /**
  22549. * Gets fired when the legend item belonging to a point is clicked. The default
  22550. * action is to toggle the visibility of the point. This can be prevented by
  22551. * returning `false` or calling `event.preventDefault()`.
  22552. *
  22553. * @callback Highcharts.PointLegendItemClickCallbackFunction
  22554. *
  22555. * @param {Highcharts.Point} this
  22556. * The point on which the event occured.
  22557. *
  22558. * @param {Highcharts.PointLegendItemClickEventObject} event
  22559. * The event that occured.
  22560. */
  22561. /**
  22562. * Information about the legend click event.
  22563. *
  22564. * @interface Highcharts.PointLegendItemClickEventObject
  22565. */ /**
  22566. * Related browser event.
  22567. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  22568. * @type {Highcharts.PointerEvent}
  22569. */ /**
  22570. * Prevent the default action of toggle the visibility of the point.
  22571. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  22572. * @type {Function}
  22573. */ /**
  22574. * Related point.
  22575. * @name Highcharts.PointLegendItemClickEventObject#target
  22576. * @type {Highcharts.Point}
  22577. */ /**
  22578. * Event type.
  22579. * @name Highcharts.PointLegendItemClickEventObject#type
  22580. * @type {"legendItemClick"}
  22581. */
  22582. /**
  22583. * Gets fired when the legend item belonging to a series is clicked. The default
  22584. * action is to toggle the visibility of the series. This can be prevented by
  22585. * returning `false` or calling `event.preventDefault()`.
  22586. *
  22587. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  22588. *
  22589. * @param {Highcharts.Series} this
  22590. * The series where the event occured.
  22591. *
  22592. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  22593. * The event that occured.
  22594. */
  22595. /**
  22596. * Information about the legend click event.
  22597. *
  22598. * @interface Highcharts.SeriesLegendItemClickEventObject
  22599. */ /**
  22600. * Related browser event.
  22601. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  22602. * @type {Highcharts.PointerEvent}
  22603. */ /**
  22604. * Prevent the default action of toggle the visibility of the series.
  22605. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  22606. * @type {Function}
  22607. */ /**
  22608. * Related series.
  22609. * @name Highcharts.SeriesLegendItemClickEventObject#target
  22610. * @type {Highcharts.Series}
  22611. */ /**
  22612. * Event type.
  22613. * @name Highcharts.SeriesLegendItemClickEventObject#type
  22614. * @type {"legendItemClick"}
  22615. */
  22616. var defined = U.defined, discardElement = U.discardElement, isNumber = U.isNumber, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation, syncTimeout = U.syncTimeout, wrap = U.wrap;
  22617. var H = Highcharts, addEvent = H.addEvent, css = H.css, fireEvent = H.fireEvent, isFirefox = H.isFirefox, marginNames = H.marginNames, merge = H.merge, stableSort = H.stableSort, win = H.win;
  22618. /* eslint-disable no-invalid-this, valid-jsdoc */
  22619. /**
  22620. * The overview of the chart's series. The legend object is instanciated
  22621. * internally in the chart constructor, and is available from the `chart.legend`
  22622. * property. Each chart has only one legend.
  22623. *
  22624. * @class
  22625. * @name Highcharts.Legend
  22626. *
  22627. * @param {Highcharts.Chart} chart
  22628. * The chart instance.
  22629. *
  22630. * @param {Highcharts.LegendOptions} options
  22631. * Legend options.
  22632. */
  22633. Highcharts.Legend = function (chart, options) {
  22634. this.init(chart, options);
  22635. };
  22636. Highcharts.Legend.prototype = {
  22637. /**
  22638. * Initialize the legend.
  22639. *
  22640. * @private
  22641. * @function Highcharts.Legend#init
  22642. *
  22643. * @param {Highcharts.Chart} chart
  22644. * The chart instance.
  22645. *
  22646. * @param {Highcharts.LegendOptions} options
  22647. * Legend options.
  22648. *
  22649. * @return {void}
  22650. */
  22651. init: function (chart, options) {
  22652. /**
  22653. * Chart of this legend.
  22654. *
  22655. * @readonly
  22656. * @name Highcharts.Legend#chart
  22657. * @type {Highcharts.Chart}
  22658. */
  22659. this.chart = chart;
  22660. this.setOptions(options);
  22661. if (options.enabled) {
  22662. // Render it
  22663. this.render();
  22664. // move checkboxes
  22665. addEvent(this.chart, 'endResize', function () {
  22666. this.legend.positionCheckboxes();
  22667. });
  22668. if (this.proximate) {
  22669. this.unchartrender = addEvent(this.chart, 'render', function () {
  22670. this.legend.proximatePositions();
  22671. this.legend.positionItems();
  22672. });
  22673. }
  22674. else if (this.unchartrender) {
  22675. this.unchartrender();
  22676. }
  22677. }
  22678. },
  22679. /**
  22680. * @private
  22681. * @function Highcharts.Legend#setOptions
  22682. * @param {Highcharts.LegendOptions} options
  22683. * @return {void}
  22684. */
  22685. setOptions: function (options) {
  22686. var padding = pick(options.padding, 8);
  22687. /**
  22688. * Legend options.
  22689. *
  22690. * @readonly
  22691. * @name Highcharts.Legend#options
  22692. * @type {Highcharts.LegendOptions}
  22693. */
  22694. this.options = options;
  22695. if (!this.chart.styledMode) {
  22696. this.itemStyle = options.itemStyle;
  22697. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  22698. }
  22699. this.itemMarginTop = options.itemMarginTop || 0;
  22700. this.itemMarginBottom = options.itemMarginBottom || 0;
  22701. this.padding = padding;
  22702. this.initialItemY = padding - 5; // 5 is pixels above the text
  22703. this.symbolWidth = pick(options.symbolWidth, 16);
  22704. this.pages = [];
  22705. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  22706. },
  22707. /**
  22708. * Update the legend with new options. Equivalent to running `chart.update`
  22709. * with a legend configuration option.
  22710. *
  22711. * @sample highcharts/legend/legend-update/
  22712. * Legend update
  22713. *
  22714. * @function Highcharts.Legend#update
  22715. *
  22716. * @param {Highcharts.LegendOptions} options
  22717. * Legend options.
  22718. *
  22719. * @param {boolean} [redraw=true]
  22720. * Whether to redraw the chart after the axis is altered. If doing
  22721. * more operations on the chart, it is a good idea to set redraw to
  22722. * false and call {@link Chart#redraw} after.
  22723. * Whether to redraw the chart.
  22724. *
  22725. * @return {void}
  22726. *
  22727. * @fires Highcharts.Legends#event:afterUpdate
  22728. */
  22729. update: function (options, redraw) {
  22730. var chart = this.chart;
  22731. this.setOptions(merge(true, this.options, options));
  22732. this.destroy();
  22733. chart.isDirtyLegend = chart.isDirtyBox = true;
  22734. if (pick(redraw, true)) {
  22735. chart.redraw();
  22736. }
  22737. fireEvent(this, 'afterUpdate');
  22738. },
  22739. /**
  22740. * Set the colors for the legend item.
  22741. *
  22742. * @private
  22743. * @function Highcharts.Legend#colorizeItem
  22744. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  22745. * A Series or Point instance
  22746. * @param {boolean} [visible=false]
  22747. * Dimmed or colored
  22748. * @return {void}
  22749. *
  22750. * @todo
  22751. * Make events official: Fires the event `afterColorizeItem`.
  22752. */
  22753. colorizeItem: function (item, visible) {
  22754. item.legendGroup[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  22755. if (!this.chart.styledMode) {
  22756. var legend = this, options = legend.options, legendItem = item.legendItem, legendLine = item.legendLine, legendSymbol = item.legendSymbol, hiddenColor = legend.itemHiddenStyle.color, textColor = visible ?
  22757. options.itemStyle.color :
  22758. hiddenColor, symbolColor = visible ?
  22759. (item.color || hiddenColor) :
  22760. hiddenColor, markerOptions = item.options && item.options.marker, symbolAttr = { fill: symbolColor };
  22761. if (legendItem) {
  22762. legendItem.css({
  22763. fill: textColor,
  22764. color: textColor // #1553, oldIE
  22765. });
  22766. }
  22767. if (legendLine) {
  22768. legendLine.attr({ stroke: symbolColor });
  22769. }
  22770. if (legendSymbol) {
  22771. // Apply marker options
  22772. if (markerOptions && legendSymbol.isMarker) { // #585
  22773. symbolAttr = item.pointAttribs();
  22774. if (!visible) {
  22775. // #6769
  22776. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  22777. }
  22778. }
  22779. legendSymbol.attr(symbolAttr);
  22780. }
  22781. }
  22782. fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
  22783. },
  22784. /**
  22785. * @private
  22786. * @function Highcharts.Legend#positionItems
  22787. * @return {void}
  22788. */
  22789. positionItems: function () {
  22790. // Now that the legend width and height are established, put the items
  22791. // in the final position
  22792. this.allItems.forEach(this.positionItem, this);
  22793. if (!this.chart.isResizing) {
  22794. this.positionCheckboxes();
  22795. }
  22796. },
  22797. /**
  22798. * Position the legend item.
  22799. *
  22800. * @private
  22801. * @function Highcharts.Legend#positionItem
  22802. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  22803. * The item to position
  22804. * @return {void}
  22805. */
  22806. positionItem: function (item) {
  22807. 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;
  22808. if (legendGroup && legendGroup.element) {
  22809. legendGroup[defined(legendGroup.translateY) ? 'animate' : 'attr']({
  22810. translateX: ltr ?
  22811. itemX :
  22812. legend.legendWidth - itemX - 2 * symbolPadding - 4,
  22813. translateY: itemY
  22814. });
  22815. }
  22816. if (checkbox) {
  22817. checkbox.x = itemX;
  22818. checkbox.y = itemY;
  22819. }
  22820. },
  22821. /**
  22822. * Destroy a single legend item, used internally on removing series items.
  22823. *
  22824. * @private
  22825. * @function Highcharts.Legend#destroyItem
  22826. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  22827. * The item to remove
  22828. * @return {void}
  22829. */
  22830. destroyItem: function (item) {
  22831. var checkbox = item.checkbox;
  22832. // destroy SVG elements
  22833. ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'].forEach(function (key) {
  22834. if (item[key]) {
  22835. item[key] = item[key].destroy();
  22836. }
  22837. });
  22838. if (checkbox) {
  22839. discardElement(item.checkbox);
  22840. }
  22841. },
  22842. /**
  22843. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  22844. * must be called after destruction.
  22845. *
  22846. * @private
  22847. * @function Highcharts.Legend#destroy
  22848. * @return {void}
  22849. */
  22850. destroy: function () {
  22851. /**
  22852. * @private
  22853. * @param {string} key
  22854. * @return {void}
  22855. */
  22856. function destroyItems(key) {
  22857. if (this[key]) {
  22858. this[key] = this[key].destroy();
  22859. }
  22860. }
  22861. // Destroy items
  22862. this.getAllItems().forEach(function (item) {
  22863. ['legendItem', 'legendGroup'].forEach(destroyItems, item);
  22864. });
  22865. // Destroy legend elements
  22866. [
  22867. 'clipRect',
  22868. 'up',
  22869. 'down',
  22870. 'pager',
  22871. 'nav',
  22872. 'box',
  22873. 'title',
  22874. 'group'
  22875. ].forEach(destroyItems, this);
  22876. this.display = null; // Reset in .render on update.
  22877. },
  22878. /**
  22879. * Position the checkboxes after the width is determined.
  22880. *
  22881. * @private
  22882. * @function Highcharts.Legend#positionCheckboxes
  22883. * @return {void}
  22884. */
  22885. positionCheckboxes: function () {
  22886. var alignAttr = this.group && this.group.alignAttr, translateY, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
  22887. if (alignAttr) {
  22888. translateY = alignAttr.translateY;
  22889. this.allItems.forEach(function (item) {
  22890. var checkbox = item.checkbox, top;
  22891. if (checkbox) {
  22892. top = translateY + titleHeight + checkbox.y +
  22893. (this.scrollOffset || 0) + 3;
  22894. css(checkbox, {
  22895. left: (alignAttr.translateX + item.checkboxOffset +
  22896. checkbox.x - 20) + 'px',
  22897. top: top + 'px',
  22898. display: this.proximate || (top > translateY - 6 &&
  22899. top < translateY + clipHeight - 6) ?
  22900. '' :
  22901. 'none'
  22902. });
  22903. }
  22904. }, this);
  22905. }
  22906. },
  22907. /**
  22908. * Render the legend title on top of the legend.
  22909. *
  22910. * @private
  22911. * @function Highcharts.Legend#renderTitle
  22912. * @return {void}
  22913. */
  22914. renderTitle: function () {
  22915. var options = this.options, padding = this.padding, titleOptions = options.title, titleHeight = 0, bBox;
  22916. if (titleOptions.text) {
  22917. if (!this.title) {
  22918. /**
  22919. * SVG element of the legend title.
  22920. *
  22921. * @readonly
  22922. * @name Highcharts.Legend#title
  22923. * @type {Highcharts.SVGElement}
  22924. */
  22925. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, options.useHTML, null, 'legend-title')
  22926. .attr({ zIndex: 1 });
  22927. if (!this.chart.styledMode) {
  22928. this.title.css(titleOptions.style);
  22929. }
  22930. this.title.add(this.group);
  22931. }
  22932. // Set the max title width (#7253)
  22933. if (!titleOptions.width) {
  22934. this.title.css({
  22935. width: this.maxLegendWidth + 'px'
  22936. });
  22937. }
  22938. bBox = this.title.getBBox();
  22939. titleHeight = bBox.height;
  22940. this.offsetWidth = bBox.width; // #1717
  22941. this.contentGroup.attr({ translateY: titleHeight });
  22942. }
  22943. this.titleHeight = titleHeight;
  22944. },
  22945. /**
  22946. * Set the legend item text.
  22947. *
  22948. * @function Highcharts.Legend#setText
  22949. *
  22950. * @param {Highcharts.Point|Highcharts.Series} item
  22951. * The item for which to update the text in the legend.
  22952. *
  22953. * @return {void}
  22954. */
  22955. setText: function (item) {
  22956. var options = this.options;
  22957. item.legendItem.attr({
  22958. text: options.labelFormat ?
  22959. H.format(options.labelFormat, item, this.chart) :
  22960. options.labelFormatter.call(item)
  22961. });
  22962. },
  22963. /**
  22964. * Render a single specific legend item. Called internally from the `render`
  22965. * function.
  22966. *
  22967. * @private
  22968. * @function Highcharts.Legend#renderItem
  22969. *
  22970. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  22971. * The item to render.
  22972. *
  22973. * @return {void}
  22974. */
  22975. renderItem: function (item) {
  22976. 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 ?
  22977. item.series :
  22978. item, seriesOptions = series.options, showCheckbox = legend.createCheckboxForItem &&
  22979. seriesOptions &&
  22980. seriesOptions.showCheckbox,
  22981. // full width minus text width
  22982. itemExtraWidth = symbolWidth + symbolPadding +
  22983. itemDistance + (showCheckbox ? 20 : 0), useHTML = options.useHTML, itemClassName = item.options.className;
  22984. if (!li) { // generate it once, later move it
  22985. // Generate the group box, a group to hold the symbol and text. Text
  22986. // is to be appended in Legend class.
  22987. item.legendGroup = renderer
  22988. .g('legend-item')
  22989. .addClass('highcharts-' + series.type + '-series ' +
  22990. 'highcharts-color-' + item.colorIndex +
  22991. (itemClassName ? ' ' + itemClassName : '') +
  22992. (isSeries ?
  22993. ' highcharts-series-' + item.index :
  22994. ''))
  22995. .attr({ zIndex: 1 })
  22996. .add(legend.scrollGroup);
  22997. // Generate the list item text and add it to the group
  22998. item.legendItem = li = renderer.text('', ltr ?
  22999. symbolWidth + symbolPadding :
  23000. -symbolPadding, legend.baseline || 0, useHTML);
  23001. if (!chart.styledMode) {
  23002. // merge to prevent modifying original (#1021)
  23003. li.css(merge(item.visible ?
  23004. itemStyle :
  23005. itemHiddenStyle));
  23006. }
  23007. li
  23008. .attr({
  23009. align: ltr ? 'left' : 'right',
  23010. zIndex: 2
  23011. })
  23012. .add(item.legendGroup);
  23013. // Get the baseline for the first item - the font size is equal for
  23014. // all
  23015. if (!legend.baseline) {
  23016. legend.fontMetrics = renderer.fontMetrics(chart.styledMode ? 12 : itemStyle.fontSize, li);
  23017. legend.baseline =
  23018. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  23019. li.attr('y', legend.baseline);
  23020. }
  23021. // Draw the legend symbol inside the group box
  23022. legend.symbolHeight =
  23023. options.symbolHeight || legend.fontMetrics.f;
  23024. series.drawLegendSymbol(legend, item);
  23025. if (legend.setItemEvents) {
  23026. legend.setItemEvents(item, li, useHTML);
  23027. }
  23028. }
  23029. // Add the HTML checkbox on top
  23030. if (showCheckbox && !item.checkbox) {
  23031. legend.createCheckboxForItem(item);
  23032. }
  23033. // Colorize the items
  23034. legend.colorizeItem(item, item.visible);
  23035. // Take care of max width and text overflow (#6659)
  23036. if (chart.styledMode || !itemStyle.width) {
  23037. li.css({
  23038. width: (options.itemWidth ||
  23039. legend.widthOption ||
  23040. chart.spacingBox.width) - itemExtraWidth
  23041. });
  23042. }
  23043. // Always update the text
  23044. legend.setText(item);
  23045. // calculate the positions for the next line
  23046. bBox = li.getBBox();
  23047. item.itemWidth = item.checkboxOffset =
  23048. options.itemWidth ||
  23049. item.legendItemWidth ||
  23050. bBox.width + itemExtraWidth;
  23051. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  23052. legend.totalItemWidth += item.itemWidth;
  23053. legend.itemHeight = item.itemHeight = Math.round(item.legendItemHeight || bBox.height || legend.symbolHeight);
  23054. },
  23055. /**
  23056. * Get the position of the item in the layout. We now know the
  23057. * maxItemWidth from the previous loop.
  23058. *
  23059. * @private
  23060. * @function Highcharts.Legend#layoutItem
  23061. *
  23062. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  23063. *
  23064. * @return {void}
  23065. */
  23066. layoutItem: function (item) {
  23067. 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 &&
  23068. this.totalItemWidth > maxLegendWidth) ?
  23069. this.maxItemWidth :
  23070. item.itemWidth;
  23071. // If the item exceeds the width, start a new line
  23072. if (horizontal &&
  23073. this.itemX - padding + itemWidth > maxLegendWidth) {
  23074. this.itemX = padding;
  23075. if (this.lastLineHeight) { // Not for the first line (#10167)
  23076. this.itemY += (itemMarginTop +
  23077. this.lastLineHeight +
  23078. itemMarginBottom);
  23079. }
  23080. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  23081. }
  23082. // Set the edge positions
  23083. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  23084. this.lastLineHeight = Math.max(// #915
  23085. itemHeight, this.lastLineHeight);
  23086. // cache the position of the newly generated or reordered items
  23087. item._legendItemPos = [this.itemX, this.itemY];
  23088. // advance
  23089. if (horizontal) {
  23090. this.itemX += itemWidth;
  23091. }
  23092. else {
  23093. this.itemY +=
  23094. itemMarginTop + itemHeight + itemMarginBottom;
  23095. this.lastLineHeight = itemHeight;
  23096. }
  23097. // the width of the widest item
  23098. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  23099. // decrease by itemDistance only when no checkbox #4853
  23100. 0 :
  23101. itemDistance) : itemWidth) + padding, this.offsetWidth);
  23102. },
  23103. /**
  23104. * Get all items, which is one item per series for most series and one
  23105. * item per point for pie series and its derivatives. Fires the event
  23106. * `afterGetAllItems`.
  23107. *
  23108. * @private
  23109. * @function Highcharts.Legend#getAllItems
  23110. * @return {Array<(Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series)>}
  23111. * The current items in the legend.
  23112. * @fires Highcharts.Legend#event:afterGetAllItems
  23113. */
  23114. getAllItems: function () {
  23115. var allItems = [];
  23116. this.chart.series.forEach(function (series) {
  23117. var seriesOptions = series && series.options;
  23118. // Handle showInLegend. If the series is linked to another series,
  23119. // defaults to false.
  23120. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  23121. // Use points or series for the legend item depending on
  23122. // legendType
  23123. allItems = allItems.concat(series.legendItems ||
  23124. (seriesOptions.legendType === 'point' ?
  23125. series.data :
  23126. series));
  23127. }
  23128. });
  23129. fireEvent(this, 'afterGetAllItems', { allItems: allItems });
  23130. return allItems;
  23131. },
  23132. /**
  23133. * Get a short, three letter string reflecting the alignment and layout.
  23134. *
  23135. * @private
  23136. * @function Highcharts.Legend#getAlignment
  23137. *
  23138. * @return {string}
  23139. * The alignment, empty string if floating
  23140. */
  23141. getAlignment: function () {
  23142. var options = this.options;
  23143. // Use the first letter of each alignment option in order to detect
  23144. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  23145. if (this.proximate) {
  23146. return options.align.charAt(0) + 'tv';
  23147. }
  23148. return options.floating ? '' : (options.align.charAt(0) +
  23149. options.verticalAlign.charAt(0) +
  23150. options.layout.charAt(0));
  23151. },
  23152. /**
  23153. * Adjust the chart margins by reserving space for the legend on only one
  23154. * side of the chart. If the position is set to a corner, top or bottom is
  23155. * reserved for horizontal legends and left or right for vertical ones.
  23156. *
  23157. * @private
  23158. * @function Highcharts.Legend#adjustMargins
  23159. * @param {Array<number>} margin
  23160. * @param {Array<number>} spacing
  23161. * @return {void}
  23162. */
  23163. adjustMargins: function (margin, spacing) {
  23164. var chart = this.chart, options = this.options, alignment = this.getAlignment();
  23165. if (alignment) {
  23166. ([
  23167. /(lth|ct|rth)/,
  23168. /(rtv|rm|rbv)/,
  23169. /(rbh|cb|lbh)/,
  23170. /(lbv|lm|ltv)/
  23171. ]).forEach(function (alignments, side) {
  23172. if (alignments.test(alignment) && !defined(margin[side])) {
  23173. // Now we have detected on which side of the chart we should
  23174. // reserve space for the legend
  23175. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  23176. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  23177. pick(options.margin, 12) +
  23178. spacing[side] +
  23179. (chart.titleOffset[side] || 0)));
  23180. }
  23181. });
  23182. }
  23183. },
  23184. /**
  23185. * @private
  23186. * @function Highcharts.Legend#proximatePositions
  23187. * @return {void}
  23188. */
  23189. proximatePositions: function () {
  23190. var chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
  23191. this.allItems.forEach(function (item) {
  23192. var lastPoint, height, useFirstPoint = alignLeft, target, top;
  23193. if (item.yAxis && item.points) {
  23194. if (item.xAxis.options.reversed) {
  23195. useFirstPoint = !useFirstPoint;
  23196. }
  23197. lastPoint = H.find(useFirstPoint ?
  23198. item.points :
  23199. item.points.slice(0).reverse(), function (item) {
  23200. return isNumber(item.plotY);
  23201. });
  23202. height = this.itemMarginTop +
  23203. item.legendItem.getBBox().height +
  23204. this.itemMarginBottom;
  23205. top = item.yAxis.top - chart.plotTop;
  23206. if (item.visible) {
  23207. target = lastPoint ?
  23208. lastPoint.plotY :
  23209. item.yAxis.height;
  23210. target += top - 0.3 * height;
  23211. }
  23212. else {
  23213. target = top + item.yAxis.height;
  23214. }
  23215. boxes.push({
  23216. target: target,
  23217. size: height,
  23218. item: item
  23219. });
  23220. }
  23221. }, this);
  23222. H.distribute(boxes, chart.plotHeight);
  23223. boxes.forEach(function (box) {
  23224. box.item._legendItemPos[1] =
  23225. chart.plotTop - chart.spacing[0] + box.pos;
  23226. });
  23227. },
  23228. /**
  23229. * Render the legend. This method can be called both before and after
  23230. * `chart.render`. If called after, it will only rearrange items instead
  23231. * of creating new ones. Called internally on initial render and after
  23232. * redraws.
  23233. *
  23234. * @private
  23235. * @function Highcharts.Legend#render
  23236. * @return {void}
  23237. */
  23238. render: function () {
  23239. 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;
  23240. legend.itemX = padding;
  23241. legend.itemY = legend.initialItemY;
  23242. legend.offsetWidth = 0;
  23243. legend.lastItemY = 0;
  23244. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  23245. // Compute how wide the legend is allowed to be
  23246. allowedWidth =
  23247. chart.spacingBox.width - 2 * padding - options.x;
  23248. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  23249. allowedWidth /= 2;
  23250. }
  23251. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  23252. if (!legendGroup) {
  23253. /**
  23254. * SVG group of the legend.
  23255. *
  23256. * @readonly
  23257. * @name Highcharts.Legend#group
  23258. * @type {Highcharts.SVGElement}
  23259. */
  23260. legend.group = legendGroup = renderer.g('legend')
  23261. .attr({ zIndex: 7 })
  23262. .add();
  23263. legend.contentGroup = renderer.g()
  23264. .attr({ zIndex: 1 }) // above background
  23265. .add(legendGroup);
  23266. legend.scrollGroup = renderer.g()
  23267. .add(legend.contentGroup);
  23268. }
  23269. legend.renderTitle();
  23270. // add each series or point
  23271. allItems = legend.getAllItems();
  23272. // sort by legendIndex
  23273. stableSort(allItems, function (a, b) {
  23274. return ((a.options && a.options.legendIndex) || 0) -
  23275. ((b.options && b.options.legendIndex) || 0);
  23276. });
  23277. // reversed legend
  23278. if (options.reversed) {
  23279. allItems.reverse();
  23280. }
  23281. /**
  23282. * All items for the legend, which is an array of series for most series
  23283. * and an array of points for pie series and its derivatives.
  23284. *
  23285. * @readonly
  23286. * @name Highcharts.Legend#allItems
  23287. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  23288. */
  23289. legend.allItems = allItems;
  23290. legend.display = display = !!allItems.length;
  23291. // Render the items. First we run a loop to set the text and properties
  23292. // and read all the bounding boxes. The next loop computes the item
  23293. // positions based on the bounding boxes.
  23294. legend.lastLineHeight = 0;
  23295. legend.maxItemWidth = 0;
  23296. legend.totalItemWidth = 0;
  23297. legend.itemHeight = 0;
  23298. allItems.forEach(legend.renderItem, legend);
  23299. allItems.forEach(legend.layoutItem, legend);
  23300. // Get the box
  23301. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  23302. legendHeight = legend.lastItemY + legend.lastLineHeight +
  23303. legend.titleHeight;
  23304. legendHeight = legend.handleOverflow(legendHeight);
  23305. legendHeight += padding;
  23306. // Draw the border and/or background
  23307. if (!box) {
  23308. /**
  23309. * SVG element of the legend box.
  23310. *
  23311. * @readonly
  23312. * @name Highcharts.Legend#box
  23313. * @type {Highcharts.SVGElement}
  23314. */
  23315. legend.box = box = renderer.rect()
  23316. .addClass('highcharts-legend-box')
  23317. .attr({
  23318. r: options.borderRadius
  23319. })
  23320. .add(legendGroup);
  23321. box.isNew = true;
  23322. }
  23323. // Presentational
  23324. if (!chart.styledMode) {
  23325. box
  23326. .attr({
  23327. stroke: options.borderColor,
  23328. 'stroke-width': options.borderWidth || 0,
  23329. fill: options.backgroundColor || 'none'
  23330. })
  23331. .shadow(options.shadow);
  23332. }
  23333. if (legendWidth > 0 && legendHeight > 0) {
  23334. box[box.isNew ? 'attr' : 'animate'](box.crisp.call({}, {
  23335. x: 0,
  23336. y: 0,
  23337. width: legendWidth,
  23338. height: legendHeight
  23339. }, box.strokeWidth()));
  23340. box.isNew = false;
  23341. }
  23342. // hide the border if no items
  23343. box[display ? 'show' : 'hide']();
  23344. // Open for responsiveness
  23345. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  23346. legendWidth = legendHeight = 0;
  23347. }
  23348. legend.legendWidth = legendWidth;
  23349. legend.legendHeight = legendHeight;
  23350. if (display) {
  23351. // If aligning to the top and the layout is horizontal, adjust for
  23352. // the title (#7428)
  23353. var alignTo = chart.spacingBox;
  23354. var y = alignTo.y;
  23355. if (/(lth|ct|rth)/.test(legend.getAlignment()) &&
  23356. chart.titleOffset[0] > 0) {
  23357. y += chart.titleOffset[0];
  23358. }
  23359. else if (/(lbh|cb|rbh)/.test(legend.getAlignment()) &&
  23360. chart.titleOffset[2] > 0) {
  23361. y -= chart.titleOffset[2];
  23362. }
  23363. if (y !== alignTo.y) {
  23364. alignTo = merge(alignTo, { y: y });
  23365. }
  23366. legendGroup.align(merge(options, {
  23367. width: legendWidth,
  23368. height: legendHeight,
  23369. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  23370. }), true, alignTo);
  23371. }
  23372. if (!this.proximate) {
  23373. this.positionItems();
  23374. }
  23375. fireEvent(this, 'afterRender');
  23376. },
  23377. /**
  23378. * Set up the overflow handling by adding navigation with up and down arrows
  23379. * below the legend.
  23380. *
  23381. * @private
  23382. * @function Highcharts.Legend#handleOverflow
  23383. * @param {number} legendHeight
  23384. * @return {number}
  23385. */
  23386. handleOverflow: function (legendHeight) {
  23387. 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 +
  23388. (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) {
  23389. if (typeof height === 'number') {
  23390. clipRect.attr({
  23391. height: height
  23392. });
  23393. }
  23394. else if (clipRect) { // Reset (#5912)
  23395. legend.clipRect = clipRect.destroy();
  23396. legend.contentGroup.clip();
  23397. }
  23398. // useHTML
  23399. if (legend.contentGroup.div) {
  23400. legend.contentGroup.div.style.clip = height ?
  23401. 'rect(' + padding + 'px,9999px,' +
  23402. (padding + height) + 'px,0)' :
  23403. 'auto';
  23404. }
  23405. }, addTracker = function (key) {
  23406. legend[key] = renderer
  23407. .circle(0, 0, arrowSize * 1.3)
  23408. .translate(arrowSize / 2, arrowSize / 2)
  23409. .add(nav);
  23410. if (!chart.styledMode) {
  23411. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  23412. }
  23413. return legend[key];
  23414. };
  23415. // Adjust the height
  23416. if (options.layout === 'horizontal' &&
  23417. options.verticalAlign !== 'middle' &&
  23418. !options.floating) {
  23419. spaceHeight /= 2;
  23420. }
  23421. if (maxHeight) {
  23422. spaceHeight = Math.min(spaceHeight, maxHeight);
  23423. }
  23424. // Reset the legend height and adjust the clipping rectangle
  23425. pages.length = 0;
  23426. if (legendHeight > spaceHeight &&
  23427. navOptions.enabled !== false) {
  23428. this.clipHeight = clipHeight =
  23429. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  23430. this.currentPage = pick(this.currentPage, 1);
  23431. this.fullHeight = legendHeight;
  23432. // Fill pages with Y positions so that the top of each a legend item
  23433. // defines the scroll top for each page (#2098)
  23434. allItems.forEach(function (item, i) {
  23435. var y = item._legendItemPos[1], h = Math.round(item.legendItem.getBBox().height), len = pages.length;
  23436. if (!len || (y - pages[len - 1] > clipHeight &&
  23437. (lastY || y) !== pages[len - 1])) {
  23438. pages.push(lastY || y);
  23439. len++;
  23440. }
  23441. // Keep track of which page each item is on
  23442. item.pageIx = len - 1;
  23443. if (lastY) {
  23444. allItems[i - 1].pageIx = len - 1;
  23445. }
  23446. if (i === allItems.length - 1 &&
  23447. y + h - pages[len - 1] > clipHeight &&
  23448. y !== lastY // #2617
  23449. ) {
  23450. pages.push(y);
  23451. item.pageIx = len;
  23452. }
  23453. if (y !== lastY) {
  23454. lastY = y;
  23455. }
  23456. });
  23457. // Only apply clipping if needed. Clipping causes blurred legend in
  23458. // PDF export (#1787)
  23459. if (!clipRect) {
  23460. clipRect = legend.clipRect =
  23461. renderer.clipRect(0, padding, 9999, 0);
  23462. legend.contentGroup.clip(clipRect);
  23463. }
  23464. clipToHeight(clipHeight);
  23465. // Add navigation elements
  23466. if (!nav) {
  23467. this.nav = nav = renderer.g()
  23468. .attr({ zIndex: 1 })
  23469. .add(this.group);
  23470. this.up = renderer
  23471. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  23472. .add(nav);
  23473. addTracker('upTracker')
  23474. .on('click', function () {
  23475. legend.scroll(-1, animation);
  23476. });
  23477. this.pager = renderer.text('', 15, 10)
  23478. .addClass('highcharts-legend-navigation');
  23479. if (!chart.styledMode) {
  23480. this.pager.css(navOptions.style);
  23481. }
  23482. this.pager.add(nav);
  23483. this.down = renderer
  23484. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  23485. .add(nav);
  23486. addTracker('downTracker')
  23487. .on('click', function () {
  23488. legend.scroll(1, animation);
  23489. });
  23490. }
  23491. // Set initial position
  23492. legend.scroll(0);
  23493. legendHeight = spaceHeight;
  23494. // Reset
  23495. }
  23496. else if (nav) {
  23497. clipToHeight();
  23498. this.nav = nav.destroy(); // #6322
  23499. this.scrollGroup.attr({
  23500. translateY: 1
  23501. });
  23502. this.clipHeight = 0; // #1379
  23503. }
  23504. return legendHeight;
  23505. },
  23506. /**
  23507. * Scroll the legend by a number of pages.
  23508. *
  23509. * @private
  23510. * @function Highcharts.Legend#scroll
  23511. *
  23512. * @param {number} scrollBy
  23513. * The number of pages to scroll.
  23514. *
  23515. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  23516. * Whether and how to apply animation.
  23517. *
  23518. * @return {void}
  23519. */
  23520. scroll: function (scrollBy, animation) {
  23521. var _this = this;
  23522. 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;
  23523. // When resizing while looking at the last page
  23524. if (currentPage > pageCount) {
  23525. currentPage = pageCount;
  23526. }
  23527. if (currentPage > 0) {
  23528. if (typeof animation !== 'undefined') {
  23529. setAnimation(animation, chart);
  23530. }
  23531. this.nav.attr({
  23532. translateX: padding,
  23533. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  23534. visibility: 'visible'
  23535. });
  23536. [this.up, this.upTracker].forEach(function (elem) {
  23537. elem.attr({
  23538. 'class': currentPage === 1 ?
  23539. 'highcharts-legend-nav-inactive' :
  23540. 'highcharts-legend-nav-active'
  23541. });
  23542. });
  23543. pager.attr({
  23544. text: currentPage + '/' + pageCount
  23545. });
  23546. [this.down, this.downTracker].forEach(function (elem) {
  23547. elem.attr({
  23548. // adjust to text width
  23549. x: 18 + this.pager.getBBox().width,
  23550. 'class': currentPage === pageCount ?
  23551. 'highcharts-legend-nav-inactive' :
  23552. 'highcharts-legend-nav-active'
  23553. });
  23554. }, this);
  23555. if (!chart.styledMode) {
  23556. this.up
  23557. .attr({
  23558. fill: currentPage === 1 ?
  23559. navOptions.inactiveColor :
  23560. navOptions.activeColor
  23561. });
  23562. this.upTracker
  23563. .css({
  23564. cursor: currentPage === 1 ? 'default' : 'pointer'
  23565. });
  23566. this.down
  23567. .attr({
  23568. fill: currentPage === pageCount ?
  23569. navOptions.inactiveColor :
  23570. navOptions.activeColor
  23571. });
  23572. this.downTracker
  23573. .css({
  23574. cursor: currentPage === pageCount ?
  23575. 'default' :
  23576. 'pointer'
  23577. });
  23578. }
  23579. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  23580. this.scrollGroup.animate({
  23581. translateY: this.scrollOffset
  23582. });
  23583. this.currentPage = currentPage;
  23584. this.positionCheckboxes();
  23585. // Fire event after scroll animation is complete
  23586. var animOptions = H.animObject(pick(animation, chart.renderer.globalAnimation, true));
  23587. syncTimeout(function () {
  23588. fireEvent(_this, 'afterScroll', { currentPage: currentPage });
  23589. }, animOptions.duration || 0);
  23590. }
  23591. }
  23592. };
  23593. /**
  23594. * Legend symbol mixin.
  23595. *
  23596. * @private
  23597. * @mixin Highcharts.LegendSymbolMixin
  23598. */
  23599. H.LegendSymbolMixin = {
  23600. /**
  23601. * Get the series' symbol in the legend
  23602. *
  23603. * @private
  23604. * @function Highcharts.LegendSymbolMixin.drawRectangle
  23605. *
  23606. * @param {Highcharts.Legend} legend
  23607. * The legend object
  23608. *
  23609. * @param {Highcharts.Point|Highcharts.Series} item
  23610. * The series (this) or point
  23611. *
  23612. * @return {void}
  23613. */
  23614. drawRectangle: function (legend, item) {
  23615. var options = legend.options, symbolHeight = legend.symbolHeight, square = options.squareSymbol, symbolWidth = square ? symbolHeight : legend.symbolWidth;
  23616. item.legendSymbol = this.chart.renderer.rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  23617. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  23618. .addClass('highcharts-point')
  23619. .attr({
  23620. zIndex: 3
  23621. }).add(item.legendGroup);
  23622. },
  23623. /**
  23624. * Get the series' symbol in the legend. This method should be overridable
  23625. * to create custom symbols through
  23626. * Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
  23627. *
  23628. * @private
  23629. * @function Highcharts.LegendSymbolMixin.drawLineMarker
  23630. *
  23631. * @param {Highcharts.Legend} legend
  23632. * The legend object.
  23633. *
  23634. * @return {void}
  23635. */
  23636. drawLineMarker: function (legend) {
  23637. 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 -
  23638. Math.round(legend.fontMetrics.b * 0.3), attr = {};
  23639. // Draw the line
  23640. if (!this.chart.styledMode) {
  23641. attr = {
  23642. 'stroke-width': options.lineWidth || 0
  23643. };
  23644. if (options.dashStyle) {
  23645. attr.dashstyle = options.dashStyle;
  23646. }
  23647. }
  23648. this.legendLine = renderer
  23649. .path([
  23650. 'M',
  23651. 0,
  23652. verticalCenter,
  23653. 'L',
  23654. symbolWidth,
  23655. verticalCenter
  23656. ])
  23657. .addClass('highcharts-graph')
  23658. .attr(attr)
  23659. .add(legendItemGroup);
  23660. // Draw the marker
  23661. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  23662. // Do not allow the marker to be larger than the symbolHeight
  23663. radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  23664. // Restrict symbol markers size
  23665. if (this.symbol.indexOf('url') === 0) {
  23666. markerOptions = merge(markerOptions, {
  23667. width: symbolHeight,
  23668. height: symbolHeight
  23669. });
  23670. radius = 0;
  23671. }
  23672. this.legendSymbol = legendSymbol = renderer.symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, markerOptions)
  23673. .addClass('highcharts-point')
  23674. .add(legendItemGroup);
  23675. legendSymbol.isMarker = true;
  23676. }
  23677. }
  23678. };
  23679. // Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
  23680. // and for #2580, a similar drawing flaw in Firefox 26.
  23681. // Explore if there's a general cause for this. The problem may be related
  23682. // to nested group elements, as the legend item texts are within 4 group
  23683. // elements.
  23684. if (/Trident\/7\.0/.test(win.navigator && win.navigator.userAgent) ||
  23685. isFirefox) {
  23686. wrap(Highcharts.Legend.prototype, 'positionItem', function (proceed, item) {
  23687. var legend = this,
  23688. // If chart destroyed in sync, this is undefined (#2030)
  23689. runPositionItem = function () {
  23690. if (item._legendItemPos) {
  23691. proceed.call(legend, item);
  23692. }
  23693. };
  23694. // Do it now, for export and to get checkbox placement
  23695. runPositionItem();
  23696. // Do it after to work around the core issue
  23697. if (!legend.bubbleLegend) {
  23698. setTimeout(runPositionItem);
  23699. }
  23700. });
  23701. }
  23702. });
  23703. _registerModule(_modules, 'parts/Chart.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  23704. /* *
  23705. *
  23706. * (c) 2010-2019 Torstein Honsi
  23707. *
  23708. * License: www.highcharts.com/license
  23709. *
  23710. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23711. *
  23712. * */
  23713. /**
  23714. * Callback for chart constructors.
  23715. *
  23716. * @callback Highcharts.ChartCallbackFunction
  23717. *
  23718. * @param {Highcharts.Chart} chart
  23719. * Created chart.
  23720. */
  23721. /**
  23722. * Format a number and return a string based on input settings.
  23723. *
  23724. * @callback Highcharts.NumberFormatterCallbackFunction
  23725. *
  23726. * @param {number} number
  23727. * The input number to format.
  23728. *
  23729. * @param {number} decimals
  23730. * The amount of decimals. A value of -1 preserves the amount in the
  23731. * input number.
  23732. *
  23733. * @param {string} [decimalPoint]
  23734. * The decimal point, defaults to the one given in the lang options, or
  23735. * a dot.
  23736. *
  23737. * @param {string} [thousandsSep]
  23738. * The thousands separator, defaults to the one given in the lang
  23739. * options, or a space character.
  23740. *
  23741. * @return {string} The formatted number.
  23742. */
  23743. /**
  23744. * The chart title. The title has an `update` method that allows modifying the
  23745. * options directly or indirectly via `chart.update`.
  23746. *
  23747. * @interface Highcharts.TitleObject
  23748. * @extends Highcharts.SVGElement
  23749. */ /**
  23750. * Modify options for the title.
  23751. *
  23752. * @function Highcharts.TitleObject#update
  23753. *
  23754. * @param {Highcharts.TitleOptions} titleOptions
  23755. * Options to modify.
  23756. *
  23757. * @param {boolean} [redraw=true]
  23758. * Whether to redraw the chart after the title is altered. If doing more
  23759. * operations on the chart, it is a good idea to set redraw to false and
  23760. * call {@link Chart#redraw} after.
  23761. */
  23762. /**
  23763. * The chart subtitle. The subtitle has an `update` method that
  23764. * allows modifying the options directly or indirectly via
  23765. * `chart.update`.
  23766. *
  23767. * @interface Highcharts.SubtitleObject
  23768. * @extends Highcharts.SVGElement
  23769. */ /**
  23770. * Modify options for the subtitle.
  23771. *
  23772. * @function Highcharts.SubtitleObject#update
  23773. *
  23774. * @param {Highcharts.SubtitleOptions} subtitleOptions
  23775. * Options to modify.
  23776. *
  23777. * @param {boolean} [redraw=true]
  23778. * Whether to redraw the chart after the subtitle is altered. If doing
  23779. * more operations on the chart, it is a good idea to set redraw to false
  23780. * and call {@link Chart#redraw} after.
  23781. */
  23782. /**
  23783. * The chart caption. The caption has an `update` method that
  23784. * allows modifying the options directly or indirectly via
  23785. * `chart.update`.
  23786. *
  23787. * @interface Highcharts.CaptionObject
  23788. * @extends Highcharts.SVGElement
  23789. */ /**
  23790. * Modify options for the caption.
  23791. *
  23792. * @function Highcharts.CaptionObject#update
  23793. *
  23794. * @param {Highcharts.CaptionOptions} captionOptions
  23795. * Options to modify.
  23796. *
  23797. * @param {boolean} [redraw=true]
  23798. * Whether to redraw the chart after the caption is altered. If doing
  23799. * more operations on the chart, it is a good idea to set redraw to false
  23800. * and call {@link Chart#redraw} after.
  23801. */
  23802. var animObject = U.animObject, attr = U.attr, defined = U.defined, discardElement = U.discardElement, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, numberFormat = U.numberFormat, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, relativeLength = U.relativeLength, setAnimation = U.setAnimation, splat = U.splat, syncTimeout = U.syncTimeout;
  23803. var addEvent = H.addEvent, animate = H.animate, doc = H.doc, Axis = H.Axis, // @todo add as requirement
  23804. createElement = H.createElement, defaultOptions = H.defaultOptions, charts = H.charts, css = H.css, find = H.find, fireEvent = H.fireEvent, Legend = H.Legend, // @todo add as requirement
  23805. marginNames = H.marginNames, merge = H.merge, Pointer = H.Pointer, // @todo add as requirement
  23806. removeEvent = H.removeEvent, seriesTypes = H.seriesTypes, win = H.win;
  23807. /* eslint-disable no-invalid-this, valid-jsdoc */
  23808. /**
  23809. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  23810. *
  23811. * @example
  23812. * var chart = Highcharts.chart('container', {
  23813. * title: {
  23814. * text: 'My chart'
  23815. * },
  23816. * series: [{
  23817. * data: [1, 3, 2, 4]
  23818. * }]
  23819. * })
  23820. *
  23821. * @class
  23822. * @name Highcharts.Chart
  23823. *
  23824. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  23825. * The DOM element to render to, or its id.
  23826. *
  23827. * @param {Highcharts.Options} options
  23828. * The chart options structure.
  23829. *
  23830. * @param {Highcharts.ChartCallbackFunction} [callback]
  23831. * Function to run when the chart has loaded and and all external images
  23832. * are loaded. Defining a
  23833. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  23834. * handler is equivalent.
  23835. */
  23836. var Chart = H.Chart = function () {
  23837. this.getArgs.apply(this, arguments);
  23838. };
  23839. /**
  23840. * Factory function for basic charts.
  23841. *
  23842. * @example
  23843. * // Render a chart in to div#container
  23844. * var chart = Highcharts.chart('container', {
  23845. * title: {
  23846. * text: 'My chart'
  23847. * },
  23848. * series: [{
  23849. * data: [1, 3, 2, 4]
  23850. * }]
  23851. * });
  23852. *
  23853. * @function Highcharts.chart
  23854. *
  23855. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  23856. * The DOM element to render to, or its id.
  23857. *
  23858. * @param {Highcharts.Options} options
  23859. * The chart options structure.
  23860. *
  23861. * @param {Highcharts.ChartCallbackFunction} [callback]
  23862. * Function to run when the chart has loaded and and all external images
  23863. * are loaded. Defining a
  23864. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  23865. * handler is equivalent.
  23866. *
  23867. * @return {Highcharts.Chart}
  23868. * Returns the Chart object.
  23869. */
  23870. H.chart = function (a, b, c) {
  23871. return new Chart(a, b, c);
  23872. };
  23873. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  23874. // Hook for adding callbacks in modules
  23875. callbacks: [],
  23876. /**
  23877. * Handle the arguments passed to the constructor.
  23878. *
  23879. * @private
  23880. * @function Highcharts.Chart#getArgs
  23881. *
  23882. * @param {...Array<*>} arguments
  23883. * All arguments for the constructor.
  23884. *
  23885. * @return {Array<*>}
  23886. * Passed arguments without renderTo.
  23887. *
  23888. * @fires Highcharts.Chart#event:init
  23889. * @fires Highcharts.Chart#event:afterInit
  23890. */
  23891. getArgs: function () {
  23892. var args = [].slice.call(arguments);
  23893. // Remove the optional first argument, renderTo, and
  23894. // set it on this.
  23895. if (isString(args[0]) || args[0].nodeName) {
  23896. this.renderTo = args.shift();
  23897. }
  23898. this.init(args[0], args[1]);
  23899. },
  23900. /**
  23901. * Overridable function that initializes the chart. The constructor's
  23902. * arguments are passed on directly.
  23903. *
  23904. * @function Highcharts.Chart#init
  23905. *
  23906. * @param {Highcharts.Options} userOptions
  23907. * Custom options.
  23908. *
  23909. * @param {Function} [callback]
  23910. * Function to run when the chart has loaded and and all external
  23911. * images are loaded.
  23912. *
  23913. * @return {void}
  23914. *
  23915. * @fires Highcharts.Chart#event:init
  23916. * @fires Highcharts.Chart#event:afterInit
  23917. */
  23918. init: function (userOptions, callback) {
  23919. // Handle regular options
  23920. var options,
  23921. // skip merging data points to increase performance
  23922. seriesOptions = userOptions.series, userPlotOptions = userOptions.plotOptions || {};
  23923. // Fire the event with a default function
  23924. fireEvent(this, 'init', { args: arguments }, function () {
  23925. userOptions.series = null;
  23926. options = merge(defaultOptions, userOptions); // do the merge
  23927. // Override (by copy of user options) or clear tooltip options
  23928. // in chart.options.plotOptions (#6218)
  23929. objectEach(options.plotOptions, function (typeOptions, type) {
  23930. if (isObject(typeOptions)) { // #8766
  23931. typeOptions.tooltip = (userPlotOptions[type] && // override by copy:
  23932. merge(userPlotOptions[type].tooltip)) || void 0; // or clear
  23933. }
  23934. });
  23935. // User options have higher priority than default options
  23936. // (#6218). In case of exporting: path is changed
  23937. options.tooltip.userOptions = (userOptions.chart &&
  23938. userOptions.chart.forExport &&
  23939. userOptions.tooltip.userOptions) || userOptions.tooltip;
  23940. // set back the series data
  23941. options.series = userOptions.series = seriesOptions;
  23942. /**
  23943. * The original options given to the constructor or a chart factory
  23944. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  23945. *
  23946. * @name Highcharts.Chart#userOptions
  23947. * @type {Highcharts.Options}
  23948. */
  23949. this.userOptions = userOptions;
  23950. var optionsChart = options.chart;
  23951. var chartEvents = optionsChart.events;
  23952. this.margin = [];
  23953. this.spacing = [];
  23954. // Pixel data bounds for touch zoom
  23955. this.bounds = { h: {}, v: {} };
  23956. // An array of functions that returns labels that should be
  23957. // considered for anti-collision
  23958. this.labelCollectors = [];
  23959. this.callback = callback;
  23960. this.isResizing = 0;
  23961. /**
  23962. * The options structure for the chart after merging
  23963. * {@link #defaultOptions} and {@link #userOptions}. It contains
  23964. * members for the sub elements like series, legend, tooltip etc.
  23965. *
  23966. * @name Highcharts.Chart#options
  23967. * @type {Highcharts.Options}
  23968. */
  23969. this.options = options;
  23970. /**
  23971. * All the axes in the chart.
  23972. *
  23973. * @see Highcharts.Chart.xAxis
  23974. * @see Highcharts.Chart.yAxis
  23975. *
  23976. * @name Highcharts.Chart#axes
  23977. * @type {Array<Highcharts.Axis>}
  23978. */
  23979. this.axes = [];
  23980. /**
  23981. * All the current series in the chart.
  23982. *
  23983. * @name Highcharts.Chart#series
  23984. * @type {Array<Highcharts.Series>}
  23985. */
  23986. this.series = [];
  23987. /**
  23988. * The `Time` object associated with the chart. Since v6.0.5,
  23989. * time settings can be applied individually for each chart. If
  23990. * no individual settings apply, the `Time` object is shared by
  23991. * all instances.
  23992. *
  23993. * @name Highcharts.Chart#time
  23994. * @type {Highcharts.Time}
  23995. */
  23996. this.time =
  23997. userOptions.time && Object.keys(userOptions.time).length ?
  23998. new H.Time(userOptions.time) :
  23999. H.time;
  24000. /**
  24001. * Callback function to override the default function that formats
  24002. * all the numbers in the chart. Returns a string with the formatted
  24003. * number.
  24004. *
  24005. * @name Highcharts.Chart#numberFormatter
  24006. * @type {Highcharts.NumberFormatterCallbackFunction}
  24007. */
  24008. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  24009. /**
  24010. * Whether the chart is in styled mode, meaning all presentatinoal
  24011. * attributes are avoided.
  24012. *
  24013. * @name Highcharts.Chart#styledMode
  24014. * @type {boolean}
  24015. */
  24016. this.styledMode = optionsChart.styledMode;
  24017. this.hasCartesianSeries = optionsChart.showAxes;
  24018. var chart = this;
  24019. /**
  24020. * Index position of the chart in the {@link Highcharts#charts}
  24021. * property.
  24022. *
  24023. * @name Highcharts.Chart#index
  24024. * @type {number}
  24025. * @readonly
  24026. */
  24027. chart.index = charts.length; // Add the chart to the global lookup
  24028. charts.push(chart);
  24029. H.chartCount++;
  24030. // Chart event handlers
  24031. if (chartEvents) {
  24032. objectEach(chartEvents, function (event, eventType) {
  24033. if (H.isFunction(event)) {
  24034. addEvent(chart, eventType, event);
  24035. }
  24036. });
  24037. }
  24038. /**
  24039. * A collection of the X axes in the chart.
  24040. *
  24041. * @name Highcharts.Chart#xAxis
  24042. * @type {Array<Highcharts.Axis>}
  24043. */
  24044. chart.xAxis = [];
  24045. /**
  24046. * A collection of the Y axes in the chart.
  24047. *
  24048. * @name Highcharts.Chart#yAxis
  24049. * @type {Array<Highcharts.Axis>}
  24050. *
  24051. * @todo
  24052. * Make events official: Fire the event `afterInit`.
  24053. */
  24054. chart.yAxis = [];
  24055. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  24056. // Fire after init but before first render, before axes and series
  24057. // have been initialized.
  24058. fireEvent(chart, 'afterInit');
  24059. chart.firstRender();
  24060. });
  24061. },
  24062. /**
  24063. * Internal function to unitialize an individual series.
  24064. *
  24065. * @private
  24066. * @function Highcharts.Chart#initSeries
  24067. *
  24068. * @param {Highcharts.SeriesOptions} options
  24069. *
  24070. * @return {Highcharts.Series}
  24071. */
  24072. initSeries: function (options) {
  24073. var chart = this, optionsChart = chart.options.chart, type = (options.type ||
  24074. optionsChart.type ||
  24075. optionsChart.defaultSeriesType), series, Constr = seriesTypes[type];
  24076. // No such series type
  24077. if (!Constr) {
  24078. H.error(17, true, chart, { missingModuleFor: type });
  24079. }
  24080. series = new Constr();
  24081. series.init(this, options);
  24082. return series;
  24083. },
  24084. /**
  24085. * Internal function to set data for all series with enabled sorting.
  24086. *
  24087. * @private
  24088. * @function Highcharts.Chart#setSeriesData
  24089. *
  24090. * @param {Highcharts.SeriesOptions} options
  24091. *
  24092. * @return {void}
  24093. */
  24094. setSeriesData: function () {
  24095. this.getSeriesOrderByLinks().forEach(function (series) {
  24096. // We need to set data for series with sorting after series init
  24097. if (!series.points && !series.data && series.enabledDataSorting) {
  24098. series.setData(series.options.data, false);
  24099. }
  24100. });
  24101. },
  24102. /**
  24103. * Sort and return chart series in order depending on the number of linked
  24104. * series.
  24105. *
  24106. * @private
  24107. * @function Highcharts.Series#getSeriesOrderByLinks
  24108. *
  24109. * @return {Array<Highcharts.Series>}
  24110. */
  24111. getSeriesOrderByLinks: function () {
  24112. return this.series.concat().sort(function (a, b) {
  24113. if (a.linkedSeries.length || b.linkedSeries.length) {
  24114. return b.linkedSeries.length - a.linkedSeries.length;
  24115. }
  24116. return 0;
  24117. });
  24118. },
  24119. /**
  24120. * Order all series above a given index. When series are added and ordered
  24121. * by configuration, only the last series is handled (#248, #1123, #2456,
  24122. * #6112). This function is called on series initialization and destroy.
  24123. *
  24124. * @private
  24125. * @function Highcharts.Series#orderSeries
  24126. * @param {number} [fromIndex]
  24127. * If this is given, only the series above this index are handled.
  24128. * @return {void}
  24129. */
  24130. orderSeries: function (fromIndex) {
  24131. var series = this.series, i = fromIndex || 0;
  24132. for (; i < series.length; i++) {
  24133. if (series[i]) {
  24134. /**
  24135. * Contains the series' index in the `Chart.series` array.
  24136. *
  24137. * @name Highcharts.Series#index
  24138. * @type {number}
  24139. * @readonly
  24140. */
  24141. series[i].index = i;
  24142. series[i].name = series[i].getName();
  24143. }
  24144. }
  24145. },
  24146. /**
  24147. * Check whether a given point is within the plot area.
  24148. *
  24149. * @function Highcharts.Chart#isInsidePlot
  24150. *
  24151. * @param {number} plotX
  24152. * Pixel x relative to the plot area.
  24153. *
  24154. * @param {number} plotY
  24155. * Pixel y relative to the plot area.
  24156. *
  24157. * @param {boolean} [inverted]
  24158. * Whether the chart is inverted.
  24159. *
  24160. * @return {boolean}
  24161. * Returns true if the given point is inside the plot area.
  24162. */
  24163. isInsidePlot: function (plotX, plotY, inverted) {
  24164. var x = inverted ? plotY : plotX, y = inverted ? plotX : plotY;
  24165. return x >= 0 &&
  24166. x <= this.plotWidth &&
  24167. y >= 0 &&
  24168. y <= this.plotHeight;
  24169. },
  24170. /**
  24171. * Redraw the chart after changes have been done to the data, axis extremes
  24172. * chart size or chart elements. All methods for updating axes, series or
  24173. * points have a parameter for redrawing the chart. This is `true` by
  24174. * default. But in many cases you want to do more than one operation on the
  24175. * chart before redrawing, for example add a number of points. In those
  24176. * cases it is a waste of resources to redraw the chart for each new point
  24177. * added. So you add the points and call `chart.redraw()` after.
  24178. *
  24179. * @function Highcharts.Chart#redraw
  24180. *
  24181. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  24182. * If or how to apply animation to the redraw.
  24183. *
  24184. * @return {void}
  24185. *
  24186. * @fires Highcharts.Chart#event:afterSetExtremes
  24187. * @fires Highcharts.Chart#event:beforeRedraw
  24188. * @fires Highcharts.Chart#event:predraw
  24189. * @fires Highcharts.Chart#event:redraw
  24190. * @fires Highcharts.Chart#event:render
  24191. * @fires Highcharts.Chart#event:updatedData
  24192. */
  24193. redraw: function (animation) {
  24194. fireEvent(this, 'beforeRedraw');
  24195. 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 = [];
  24196. // Handle responsive rules, not only on resize (#6130)
  24197. if (chart.setResponsive) {
  24198. chart.setResponsive(false);
  24199. }
  24200. setAnimation(animation, chart);
  24201. if (isHiddenChart) {
  24202. chart.temporaryDisplay();
  24203. }
  24204. // Adjust title layout (reflow multiline text)
  24205. chart.layOutTitles();
  24206. // link stacked series
  24207. i = series.length;
  24208. while (i--) {
  24209. serie = series[i];
  24210. if (serie.options.stacking) {
  24211. hasStackedSeries = true;
  24212. if (serie.isDirty) {
  24213. hasDirtyStacks = true;
  24214. break;
  24215. }
  24216. }
  24217. }
  24218. if (hasDirtyStacks) { // mark others as dirty
  24219. i = series.length;
  24220. while (i--) {
  24221. serie = series[i];
  24222. if (serie.options.stacking) {
  24223. serie.isDirty = true;
  24224. }
  24225. }
  24226. }
  24227. // Handle updated data in the series
  24228. series.forEach(function (serie) {
  24229. if (serie.isDirty) {
  24230. if (serie.options.legendType === 'point') {
  24231. if (serie.updateTotals) {
  24232. serie.updateTotals();
  24233. }
  24234. redrawLegend = true;
  24235. }
  24236. else if (legendUserOptions &&
  24237. (legendUserOptions.labelFormatter ||
  24238. legendUserOptions.labelFormat)) {
  24239. redrawLegend = true; // #2165
  24240. }
  24241. }
  24242. if (serie.isDirtyData) {
  24243. fireEvent(serie, 'updatedData');
  24244. }
  24245. });
  24246. // handle added or removed series
  24247. if (redrawLegend && legend && legend.options.enabled) {
  24248. // draw legend graphics
  24249. legend.render();
  24250. chart.isDirtyLegend = false;
  24251. }
  24252. // reset stacks
  24253. if (hasStackedSeries) {
  24254. chart.getStacks();
  24255. }
  24256. if (hasCartesianSeries) {
  24257. // set axes scales
  24258. axes.forEach(function (axis) {
  24259. axis.updateNames();
  24260. axis.setScale();
  24261. });
  24262. }
  24263. chart.getMargins(); // #3098
  24264. if (hasCartesianSeries) {
  24265. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  24266. axes.forEach(function (axis) {
  24267. if (axis.isDirty) {
  24268. isDirtyBox = true;
  24269. }
  24270. });
  24271. // redraw axes
  24272. axes.forEach(function (axis) {
  24273. // Fire 'afterSetExtremes' only if extremes are set
  24274. var key = axis.min + ',' + axis.max;
  24275. if (axis.extKey !== key) { // #821, #4452
  24276. axis.extKey = key;
  24277. // prevent a recursive call to chart.redraw() (#1119)
  24278. afterRedraw.push(function () {
  24279. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  24280. delete axis.eventArgs;
  24281. });
  24282. }
  24283. if (isDirtyBox || hasStackedSeries) {
  24284. axis.redraw();
  24285. }
  24286. });
  24287. }
  24288. // the plot areas size has changed
  24289. if (isDirtyBox) {
  24290. chart.drawChartBox();
  24291. }
  24292. // Fire an event before redrawing series, used by the boost module to
  24293. // clear previous series renderings.
  24294. fireEvent(chart, 'predraw');
  24295. // redraw affected series
  24296. series.forEach(function (serie) {
  24297. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  24298. serie.redraw();
  24299. }
  24300. // Set it here, otherwise we will have unlimited 'updatedData' calls
  24301. // for a hidden series after setData(). Fixes #6012
  24302. serie.isDirtyData = false;
  24303. });
  24304. // move tooltip or reset
  24305. if (pointer) {
  24306. pointer.reset(true);
  24307. }
  24308. // redraw if canvas
  24309. renderer.draw();
  24310. // Fire the events
  24311. fireEvent(chart, 'redraw');
  24312. fireEvent(chart, 'render');
  24313. if (isHiddenChart) {
  24314. chart.temporaryDisplay(true);
  24315. }
  24316. // Fire callbacks that are put on hold until after the redraw
  24317. afterRedraw.forEach(function (callback) {
  24318. callback.call();
  24319. });
  24320. },
  24321. /**
  24322. * Get an axis, series or point object by `id` as given in the configuration
  24323. * options. Returns `undefined` if no item is found.
  24324. *
  24325. * @sample highcharts/plotoptions/series-id/
  24326. * Get series by id
  24327. *
  24328. * @function Highcharts.Chart#get
  24329. *
  24330. * @param {string} id
  24331. * The id as given in the configuration options.
  24332. *
  24333. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  24334. * The retrieved item.
  24335. */
  24336. get: function (id) {
  24337. var ret, series = this.series, i;
  24338. /**
  24339. * @private
  24340. * @param {Highcharts.Axis|Highcharts.Series} item
  24341. * @return {boolean}
  24342. */
  24343. function itemById(item) {
  24344. return (item.id === id ||
  24345. (item.options && item.options.id === id));
  24346. }
  24347. ret =
  24348. // Search axes
  24349. find(this.axes, itemById) ||
  24350. // Search series
  24351. find(this.series, itemById);
  24352. // Search points
  24353. for (i = 0; !ret && i < series.length; i++) {
  24354. ret = find(series[i].points || [], itemById);
  24355. }
  24356. return ret;
  24357. },
  24358. /**
  24359. * Create the Axis instances based on the config options.
  24360. *
  24361. * @private
  24362. * @function Highcharts.Chart#getAxes
  24363. *
  24364. * @return {void}
  24365. *
  24366. * @fires Highcharts.Chart#event:afterGetAxes
  24367. * @fires Highcharts.Chart#event:getAxes
  24368. */
  24369. getAxes: function () {
  24370. var chart = this, options = this.options, xAxisOptions = options.xAxis = splat(options.xAxis || {}), yAxisOptions = options.yAxis = splat(options.yAxis || {}), optionsArray;
  24371. fireEvent(this, 'getAxes');
  24372. // make sure the options are arrays and add some members
  24373. xAxisOptions.forEach(function (axis, i) {
  24374. axis.index = i;
  24375. axis.isX = true;
  24376. });
  24377. yAxisOptions.forEach(function (axis, i) {
  24378. axis.index = i;
  24379. });
  24380. // concatenate all axis options into one array
  24381. optionsArray = xAxisOptions.concat(yAxisOptions);
  24382. optionsArray.forEach(function (axisOptions) {
  24383. new Axis(chart, axisOptions); // eslint-disable-line no-new
  24384. });
  24385. fireEvent(this, 'afterGetAxes');
  24386. },
  24387. /**
  24388. * Returns an array of all currently selected points in the chart. Points
  24389. * can be selected by clicking or programmatically by the
  24390. * {@link Highcharts.Point#select}
  24391. * function.
  24392. *
  24393. * @sample highcharts/plotoptions/series-allowpointselect-line/
  24394. * Get selected points
  24395. *
  24396. * @function Highcharts.Chart#getSelectedPoints
  24397. *
  24398. * @return {Array<Highcharts.Point>}
  24399. * The currently selected points.
  24400. */
  24401. getSelectedPoints: function () {
  24402. var points = [];
  24403. this.series.forEach(function (serie) {
  24404. // For one-to-one points inspect series.data in order to retrieve
  24405. // points outside the visible range (#6445). For grouped data,
  24406. // inspect the generated series.points.
  24407. points = points.concat((serie[serie.hasGroupedData ? 'points' : 'data'] || []).filter(function (point) {
  24408. return pick(point.selectedStaging, point.selected);
  24409. }));
  24410. });
  24411. return points;
  24412. },
  24413. /**
  24414. * Returns an array of all currently selected series in the chart. Series
  24415. * can be selected either programmatically by the
  24416. * {@link Highcharts.Series#select}
  24417. * function or by checking the checkbox next to the legend item if
  24418. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  24419. * is true.
  24420. *
  24421. * @sample highcharts/members/chart-getselectedseries/
  24422. * Get selected series
  24423. *
  24424. * @function Highcharts.Chart#getSelectedSeries
  24425. *
  24426. * @return {Array<Highcharts.Series>}
  24427. * The currently selected series.
  24428. */
  24429. getSelectedSeries: function () {
  24430. return this.series.filter(function (serie) {
  24431. return serie.selected;
  24432. });
  24433. },
  24434. /**
  24435. * Set a new title or subtitle for the chart.
  24436. *
  24437. * @sample highcharts/members/chart-settitle/
  24438. * Set title text and styles
  24439. *
  24440. * @function Highcharts.Chart#setTitle
  24441. *
  24442. * @param {Highcharts.TitleOptions} [titleOptions]
  24443. * New title options. The title text itself is set by the
  24444. * `titleOptions.text` property.
  24445. *
  24446. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  24447. * New subtitle options. The subtitle text itself is set by the
  24448. * `subtitleOptions.text` property.
  24449. *
  24450. * @param {boolean} [redraw]
  24451. * Whether to redraw the chart or wait for a later call to
  24452. * `chart.redraw()`.
  24453. *
  24454. * @return {void}
  24455. */
  24456. setTitle: function (titleOptions, subtitleOptions, redraw) {
  24457. this.applyDescription('title', titleOptions);
  24458. this.applyDescription('subtitle', subtitleOptions);
  24459. // The initial call also adds the caption. On update, chart.update will
  24460. // relay to Chart.setCaption.
  24461. this.applyDescription('caption', void 0);
  24462. this.layOutTitles(redraw);
  24463. },
  24464. /**
  24465. * Apply a title, subtitle or caption for the chart
  24466. *
  24467. * @private
  24468. * @function Highcharts.Chart#applyDescription
  24469. *
  24470. * @param name {string}
  24471. * Either title, subtitle or caption
  24472. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  24473. * The options to set, will be merged with default options.
  24474. *
  24475. * @return {void}
  24476. */
  24477. applyDescription: function (name, explicitOptions) {
  24478. var chart = this;
  24479. // Default style
  24480. var style = name === 'title' ? {
  24481. color: '#333333',
  24482. fontSize: this.options.isStock ? '16px' : '18px' // #2944
  24483. } : {
  24484. color: '#666666'
  24485. };
  24486. // Merge default options with explicit options
  24487. var options = this.options[name] = merge(
  24488. // Default styles
  24489. (!this.styledMode && { style: style }), this.options[name], explicitOptions);
  24490. var elem = this[name];
  24491. if (elem && explicitOptions) {
  24492. this[name] = elem = elem.destroy(); // remove old
  24493. }
  24494. if (options && !elem) {
  24495. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  24496. .attr({
  24497. align: options.align,
  24498. 'class': 'highcharts-' + name,
  24499. zIndex: options.zIndex || 4
  24500. })
  24501. .add();
  24502. // Update methods, shortcut to Chart.setTitle, Chart.setSubtitle and
  24503. // Chart.setCaption
  24504. elem.update = function (updateOptions) {
  24505. var fn = {
  24506. title: 'setTitle',
  24507. subtitle: 'setSubtitle',
  24508. caption: 'setCaption'
  24509. }[name];
  24510. chart[fn](updateOptions);
  24511. };
  24512. // Presentational
  24513. if (!this.styledMode) {
  24514. elem.css(options.style);
  24515. }
  24516. /**
  24517. * The chart title. The title has an `update` method that allows
  24518. * modifying the options directly or indirectly via
  24519. * `chart.update`.
  24520. *
  24521. * @sample highcharts/members/title-update/
  24522. * Updating titles
  24523. *
  24524. * @name Highcharts.Chart#title
  24525. * @type {Highcharts.TitleObject}
  24526. */
  24527. /**
  24528. * The chart subtitle. The subtitle has an `update` method that
  24529. * allows modifying the options directly or indirectly via
  24530. * `chart.update`.
  24531. *
  24532. * @name Highcharts.Chart#subtitle
  24533. * @type {Highcharts.SubtitleObject}
  24534. */
  24535. this[name] = elem;
  24536. }
  24537. },
  24538. /**
  24539. * Internal function to lay out the chart title, subtitle and caption, and
  24540. * cache the full offset height for use in `getMargins`. The result is
  24541. * stored in `this.titleOffset`.
  24542. *
  24543. * @private
  24544. * @function Highcharts.Chart#layOutTitles
  24545. *
  24546. * @param {boolean} [redraw=true]
  24547. *
  24548. * @return {void}
  24549. *
  24550. * @fires Highcharts.Chart#event:afterLayOutTitles
  24551. */
  24552. layOutTitles: function (redraw) {
  24553. var titleOffset = [0, 0, 0], requiresDirtyBox, renderer = this.renderer, spacingBox = this.spacingBox;
  24554. // Lay out the title and the subtitle respectively
  24555. ['title', 'subtitle', 'caption'].forEach(function (key) {
  24556. var title = this[key], titleOptions = this.options[key], verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ? -3 :
  24557. // Floating subtitle (#6574)
  24558. verticalAlign === 'top' ? titleOffset[0] + 2 : 0, titleSize, height;
  24559. if (title) {
  24560. if (!this.styledMode) {
  24561. titleSize = titleOptions.style.fontSize;
  24562. }
  24563. titleSize = renderer.fontMetrics(titleSize, title).b;
  24564. title
  24565. .css({
  24566. width: (titleOptions.width ||
  24567. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  24568. });
  24569. // Skip the cache for HTML (#3481, #11666)
  24570. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  24571. title.align(extend({
  24572. y: verticalAlign === 'bottom' ?
  24573. titleSize :
  24574. offset + titleSize,
  24575. height: height
  24576. }, titleOptions), false, 'spacingBox');
  24577. if (!titleOptions.floating) {
  24578. if (verticalAlign === 'top') {
  24579. titleOffset[0] = Math.ceil(titleOffset[0] +
  24580. height);
  24581. }
  24582. else if (verticalAlign === 'bottom') {
  24583. titleOffset[2] = Math.ceil(titleOffset[2] +
  24584. height);
  24585. }
  24586. }
  24587. }
  24588. }, this);
  24589. // Handle title.margin and caption.margin
  24590. if (titleOffset[0] &&
  24591. (this.options.title.verticalAlign || 'top') === 'top') {
  24592. titleOffset[0] += this.options.title.margin;
  24593. }
  24594. if (titleOffset[2] &&
  24595. this.options.caption.verticalAlign === 'bottom') {
  24596. titleOffset[2] += this.options.caption.margin;
  24597. }
  24598. requiresDirtyBox = (!this.titleOffset ||
  24599. this.titleOffset.join(',') !== titleOffset.join(','));
  24600. // Used in getMargins
  24601. this.titleOffset = titleOffset;
  24602. fireEvent(this, 'afterLayOutTitles');
  24603. if (!this.isDirtyBox && requiresDirtyBox) {
  24604. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  24605. // Redraw if necessary (#2719, #2744)
  24606. if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
  24607. this.redraw();
  24608. }
  24609. }
  24610. },
  24611. /**
  24612. * Internal function to get the chart width and height according to options
  24613. * and container size. Sets {@link Chart.chartWidth} and
  24614. * {@link Chart.chartHeight}.
  24615. *
  24616. * @private
  24617. * @function Highcharts.Chart#getChartSize
  24618. *
  24619. * @return {void}
  24620. */
  24621. getChartSize: function () {
  24622. var chart = this, optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, renderTo = chart.renderTo;
  24623. // Get inner width and height
  24624. if (!defined(widthOption)) {
  24625. chart.containerWidth = H.getStyle(renderTo, 'width');
  24626. }
  24627. if (!defined(heightOption)) {
  24628. chart.containerHeight = H.getStyle(renderTo, 'height');
  24629. }
  24630. /**
  24631. * The current pixel width of the chart.
  24632. *
  24633. * @name Highcharts.Chart#chartWidth
  24634. * @type {number}
  24635. */
  24636. chart.chartWidth = Math.max(// #1393
  24637. 0, widthOption || chart.containerWidth || 600 // #1460
  24638. );
  24639. /**
  24640. * The current pixel height of the chart.
  24641. *
  24642. * @name Highcharts.Chart#chartHeight
  24643. * @type {number}
  24644. */
  24645. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  24646. (chart.containerHeight > 1 ?
  24647. chart.containerHeight :
  24648. 400));
  24649. },
  24650. /**
  24651. * If the renderTo element has no offsetWidth, most likely one or more of
  24652. * its parents are hidden. Loop up the DOM tree to temporarily display the
  24653. * parents, then save the original display properties, and when the true
  24654. * size is retrieved, reset them. Used on first render and on redraws.
  24655. *
  24656. * @private
  24657. * @function Highcharts.Chart#temporaryDisplay
  24658. *
  24659. * @param {boolean} [revert]
  24660. * Revert to the saved original styles.
  24661. *
  24662. * @return {void}
  24663. */
  24664. temporaryDisplay: function (revert) {
  24665. var node = this.renderTo, tempStyle;
  24666. if (!revert) {
  24667. while (node && node.style) {
  24668. // When rendering to a detached node, it needs to be temporarily
  24669. // attached in order to read styling and bounding boxes (#5783,
  24670. // #7024).
  24671. if (!doc.body.contains(node) && !node.parentNode) {
  24672. node.hcOrigDetached = true;
  24673. doc.body.appendChild(node);
  24674. }
  24675. if (H.getStyle(node, 'display', false) === 'none' ||
  24676. node.hcOricDetached) {
  24677. node.hcOrigStyle = {
  24678. display: node.style.display,
  24679. height: node.style.height,
  24680. overflow: node.style.overflow
  24681. };
  24682. tempStyle = {
  24683. display: 'block',
  24684. overflow: 'hidden'
  24685. };
  24686. if (node !== this.renderTo) {
  24687. tempStyle.height = 0;
  24688. }
  24689. H.css(node, tempStyle);
  24690. // If it still doesn't have an offset width after setting
  24691. // display to block, it probably has an !important priority
  24692. // #2631, 6803
  24693. if (!node.offsetWidth) {
  24694. node.style.setProperty('display', 'block', 'important');
  24695. }
  24696. }
  24697. node = node.parentNode;
  24698. if (node === doc.body) {
  24699. break;
  24700. }
  24701. }
  24702. }
  24703. else {
  24704. while (node && node.style) {
  24705. if (node.hcOrigStyle) {
  24706. H.css(node, node.hcOrigStyle);
  24707. delete node.hcOrigStyle;
  24708. }
  24709. if (node.hcOrigDetached) {
  24710. doc.body.removeChild(node);
  24711. node.hcOrigDetached = false;
  24712. }
  24713. node = node.parentNode;
  24714. }
  24715. }
  24716. },
  24717. /**
  24718. * Set the {@link Chart.container|chart container's} class name, in
  24719. * addition to `highcharts-container`.
  24720. *
  24721. * @function Highcharts.Chart#setClassName
  24722. *
  24723. * @param {string} [className]
  24724. *
  24725. * @return {void}
  24726. */
  24727. setClassName: function (className) {
  24728. this.container.className = 'highcharts-container ' + (className || '');
  24729. },
  24730. /**
  24731. * Get the containing element, determine the size and create the inner
  24732. * container div to hold the chart.
  24733. *
  24734. * @private
  24735. * @function Highcharts.Chart#afterGetContainer
  24736. *
  24737. * @return {void}
  24738. *
  24739. * @fires Highcharts.Chart#event:afterGetContainer
  24740. */
  24741. getContainer: function () {
  24742. var chart = this, container, options = chart.options, optionsChart = options.chart, chartWidth, chartHeight, renderTo = chart.renderTo, indexAttrName = 'data-highcharts-chart', oldChartIndex, Ren, containerId = H.uniqueKey(), containerStyle, key;
  24743. if (!renderTo) {
  24744. chart.renderTo = renderTo =
  24745. optionsChart.renderTo;
  24746. }
  24747. if (isString(renderTo)) {
  24748. chart.renderTo = renderTo =
  24749. doc.getElementById(renderTo);
  24750. }
  24751. // Display an error if the renderTo is wrong
  24752. if (!renderTo) {
  24753. H.error(13, true, chart);
  24754. }
  24755. // If the container already holds a chart, destroy it. The check for
  24756. // hasRendered is there because web pages that are saved to disk from
  24757. // the browser, will preserve the data-highcharts-chart attribute and
  24758. // the SVG contents, but not an interactive chart. So in this case,
  24759. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  24760. oldChartIndex = pInt(attr(renderTo, indexAttrName));
  24761. if (isNumber(oldChartIndex) &&
  24762. charts[oldChartIndex] &&
  24763. charts[oldChartIndex].hasRendered) {
  24764. charts[oldChartIndex].destroy();
  24765. }
  24766. // Make a reference to the chart from the div
  24767. attr(renderTo, indexAttrName, chart.index);
  24768. // remove previous chart
  24769. renderTo.innerHTML = '';
  24770. // If the container doesn't have an offsetWidth, it has or is a child of
  24771. // a node that has display:none. We need to temporarily move it out to a
  24772. // visible state to determine the size, else the legend and tooltips
  24773. // won't render properly. The skipClone option is used in sparklines as
  24774. // a micro optimization, saving about 1-2 ms each chart.
  24775. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  24776. chart.temporaryDisplay();
  24777. }
  24778. // get the width and height
  24779. chart.getChartSize();
  24780. chartWidth = chart.chartWidth;
  24781. chartHeight = chart.chartHeight;
  24782. // Allow table cells and flex-boxes to shrink without the chart blocking
  24783. // them out (#6427)
  24784. css(renderTo, { overflow: 'hidden' });
  24785. // Create the inner container
  24786. if (!chart.styledMode) {
  24787. containerStyle = extend({
  24788. position: 'relative',
  24789. // needed for context menu (avoidscrollbars) and content
  24790. // overflow in IE
  24791. overflow: 'hidden',
  24792. width: chartWidth + 'px',
  24793. height: chartHeight + 'px',
  24794. textAlign: 'left',
  24795. lineHeight: 'normal',
  24796. zIndex: 0,
  24797. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
  24798. }, optionsChart.style);
  24799. }
  24800. /**
  24801. * The containing HTML element of the chart. The container is
  24802. * dynamically inserted into the element given as the `renderTo`
  24803. * parameter in the {@link Highcharts#chart} constructor.
  24804. *
  24805. * @name Highcharts.Chart#container
  24806. * @type {Highcharts.HTMLDOMElement}
  24807. */
  24808. container = createElement('div', {
  24809. id: containerId
  24810. }, containerStyle, renderTo);
  24811. chart.container = container;
  24812. // cache the cursor (#1650)
  24813. chart._cursor = container.style.cursor;
  24814. // Initialize the renderer
  24815. Ren = H[optionsChart.renderer] || H.Renderer;
  24816. /**
  24817. * The renderer instance of the chart. Each chart instance has only one
  24818. * associated renderer.
  24819. *
  24820. * @name Highcharts.Chart#renderer
  24821. * @type {Highcharts.SVGRenderer}
  24822. */
  24823. chart.renderer = new Ren(container, chartWidth, chartHeight, null, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  24824. chart.setClassName(optionsChart.className);
  24825. if (!chart.styledMode) {
  24826. chart.renderer.setStyle(optionsChart.style);
  24827. }
  24828. else {
  24829. // Initialize definitions
  24830. for (key in options.defs) { // eslint-disable-line guard-for-in
  24831. this.renderer.definition(options.defs[key]);
  24832. }
  24833. }
  24834. // Add a reference to the charts index
  24835. chart.renderer.chartIndex = chart.index;
  24836. fireEvent(this, 'afterGetContainer');
  24837. },
  24838. /**
  24839. * Calculate margins by rendering axis labels in a preliminary position.
  24840. * Title, subtitle and legend have already been rendered at this stage, but
  24841. * will be moved into their final positions.
  24842. *
  24843. * @private
  24844. * @function Highcharts.Chart#getMargins
  24845. * @param {boolean} skipAxes
  24846. * @return {void}
  24847. * @fires Highcharts.Chart#event:getMargins
  24848. */
  24849. getMargins: function (skipAxes) {
  24850. var _a = this, spacing = _a.spacing, margin = _a.margin, titleOffset = _a.titleOffset;
  24851. this.resetMargins();
  24852. // Adjust for title and subtitle
  24853. if (titleOffset[0] && !defined(margin[0])) {
  24854. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  24855. }
  24856. if (titleOffset[2] && !defined(margin[2])) {
  24857. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  24858. }
  24859. // Adjust for legend
  24860. if (this.legend && this.legend.display) {
  24861. this.legend.adjustMargins(margin, spacing);
  24862. }
  24863. fireEvent(this, 'getMargins');
  24864. if (!skipAxes) {
  24865. this.getAxisMargins();
  24866. }
  24867. },
  24868. /**
  24869. * @private
  24870. * @function Highcharts.Chart#getAxisMargins
  24871. * @return {void}
  24872. */
  24873. getAxisMargins: function () {
  24874. var chart = this,
  24875. // [top, right, bottom, left]
  24876. axisOffset = chart.axisOffset = [0, 0, 0, 0], colorAxis = chart.colorAxis, margin = chart.margin, getOffset = function (axes) {
  24877. axes.forEach(function (axis) {
  24878. if (axis.visible) {
  24879. axis.getOffset();
  24880. }
  24881. });
  24882. };
  24883. // pre-render axes to get labels offset width
  24884. if (chart.hasCartesianSeries) {
  24885. getOffset(chart.axes);
  24886. }
  24887. else if (colorAxis && colorAxis.length) {
  24888. getOffset(colorAxis);
  24889. }
  24890. // Add the axis offsets
  24891. marginNames.forEach(function (m, side) {
  24892. if (!defined(margin[side])) {
  24893. chart[m] += axisOffset[side];
  24894. }
  24895. });
  24896. chart.setChartSize();
  24897. },
  24898. /**
  24899. * Reflows the chart to its container. By default, the chart reflows
  24900. * automatically to its container following a `window.resize` event, as per
  24901. * the [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  24902. * option. However, there are no reliable events for div resize, so if the
  24903. * container is resized without a window resize event, this must be called
  24904. * explicitly.
  24905. *
  24906. * @sample highcharts/members/chart-reflow/
  24907. * Resize div and reflow
  24908. * @sample highcharts/chart/events-container/
  24909. * Pop up and reflow
  24910. *
  24911. * @function Highcharts.Chart#reflow
  24912. *
  24913. * @param {global.Event} [e]
  24914. * Event arguments. Used primarily when the function is called
  24915. * internally as a response to window resize.
  24916. */
  24917. reflow: function (e) {
  24918. var chart = this, optionsChart = chart.options.chart, renderTo = chart.renderTo, hasUserSize = (defined(optionsChart.width) &&
  24919. defined(optionsChart.height)), width = optionsChart.width || H.getStyle(renderTo, 'width'), height = optionsChart.height || H.getStyle(renderTo, 'height'), target = e ? e.target : win;
  24920. // Width and height checks for display:none. Target is doc in IE8 and
  24921. // Opera, win in Firefox, Chrome and IE9.
  24922. if (!hasUserSize &&
  24923. !chart.isPrinting &&
  24924. width &&
  24925. height &&
  24926. (target === win || target === doc)) {
  24927. if (width !== chart.containerWidth ||
  24928. height !== chart.containerHeight) {
  24929. H.clearTimeout(chart.reflowTimeout);
  24930. // When called from window.resize, e is set, else it's called
  24931. // directly (#2224)
  24932. chart.reflowTimeout = syncTimeout(function () {
  24933. // Set size, it may have been destroyed in the meantime
  24934. // (#1257)
  24935. if (chart.container) {
  24936. chart.setSize(void 0, void 0, false);
  24937. }
  24938. }, e ? 100 : 0);
  24939. }
  24940. chart.containerWidth = width;
  24941. chart.containerHeight = height;
  24942. }
  24943. },
  24944. /**
  24945. * Toggle the event handlers necessary for auto resizing, depending on the
  24946. * `chart.reflow` option.
  24947. *
  24948. * @private
  24949. * @function Highcharts.Chart#setReflow
  24950. * @param {boolean} [reflow]
  24951. * @return {void}
  24952. */
  24953. setReflow: function (reflow) {
  24954. var chart = this;
  24955. if (reflow !== false && !this.unbindReflow) {
  24956. this.unbindReflow = addEvent(win, 'resize', function (e) {
  24957. // a removed event listener still runs in Edge and IE if the
  24958. // listener was removed while the event runs, so check if the
  24959. // chart is not destroyed (#11609)
  24960. if (chart.options) {
  24961. chart.reflow(e);
  24962. }
  24963. });
  24964. addEvent(this, 'destroy', this.unbindReflow);
  24965. }
  24966. else if (reflow === false && this.unbindReflow) {
  24967. // Unbind and unset
  24968. this.unbindReflow = this.unbindReflow();
  24969. }
  24970. // The following will add listeners to re-fit the chart before and after
  24971. // printing (#2284). However it only works in WebKit. Should have worked
  24972. // in Firefox, but not supported in IE.
  24973. /*
  24974. if (win.matchMedia) {
  24975. win.matchMedia('print').addListener(function reflow() {
  24976. chart.reflow();
  24977. });
  24978. }
  24979. //*/
  24980. },
  24981. /**
  24982. * Resize the chart to a given width and height. In order to set the width
  24983. * only, the height argument may be skipped. To set the height only, pass
  24984. * `undefined` for the width.
  24985. *
  24986. * @sample highcharts/members/chart-setsize-button/
  24987. * Test resizing from buttons
  24988. * @sample highcharts/members/chart-setsize-jquery-resizable/
  24989. * Add a jQuery UI resizable
  24990. * @sample stock/members/chart-setsize/
  24991. * Highstock with UI resizable
  24992. *
  24993. * @function Highcharts.Chart#setSize
  24994. *
  24995. * @param {number|null} [width]
  24996. * The new pixel width of the chart. Since v4.2.6, the argument can
  24997. * be `undefined` in order to preserve the current value (when
  24998. * setting height only), or `null` to adapt to the width of the
  24999. * containing element.
  25000. *
  25001. * @param {number|null} [height]
  25002. * The new pixel height of the chart. Since v4.2.6, the argument can
  25003. * be `undefined` in order to preserve the current value, or `null`
  25004. * in order to adapt to the height of the containing element.
  25005. *
  25006. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  25007. * Whether and how to apply animation.
  25008. *
  25009. * @return {void}
  25010. *
  25011. * @fires Highcharts.Chart#event:endResize
  25012. * @fires Highcharts.Chart#event:resize
  25013. */
  25014. setSize: function (width, height, animation) {
  25015. var chart = this, renderer = chart.renderer, globalAnimation;
  25016. // Handle the isResizing counter
  25017. chart.isResizing += 1;
  25018. // set the animation for the current process
  25019. setAnimation(animation, chart);
  25020. chart.oldChartHeight = chart.chartHeight;
  25021. chart.oldChartWidth = chart.chartWidth;
  25022. if (typeof width !== 'undefined') {
  25023. chart.options.chart.width = width;
  25024. }
  25025. if (typeof height !== 'undefined') {
  25026. chart.options.chart.height = height;
  25027. }
  25028. chart.getChartSize();
  25029. // Resize the container with the global animation applied if enabled
  25030. // (#2503)
  25031. if (!chart.styledMode) {
  25032. globalAnimation = renderer.globalAnimation;
  25033. (globalAnimation ? animate : css)(chart.container, {
  25034. width: chart.chartWidth + 'px',
  25035. height: chart.chartHeight + 'px'
  25036. }, globalAnimation);
  25037. }
  25038. chart.setChartSize(true);
  25039. renderer.setSize(chart.chartWidth, chart.chartHeight, animation);
  25040. // handle axes
  25041. chart.axes.forEach(function (axis) {
  25042. axis.isDirty = true;
  25043. axis.setScale();
  25044. });
  25045. chart.isDirtyLegend = true; // force legend redraw
  25046. chart.isDirtyBox = true; // force redraw of plot and chart border
  25047. chart.layOutTitles(); // #2857
  25048. chart.getMargins();
  25049. chart.redraw(animation);
  25050. chart.oldChartHeight = null;
  25051. fireEvent(chart, 'resize');
  25052. // Fire endResize and set isResizing back. If animation is disabled,
  25053. // fire without delay
  25054. syncTimeout(function () {
  25055. if (chart) {
  25056. fireEvent(chart, 'endResize', null, function () {
  25057. chart.isResizing -= 1;
  25058. });
  25059. }
  25060. }, animObject(globalAnimation).duration || 0);
  25061. },
  25062. /**
  25063. * Set the public chart properties. This is done before and after the
  25064. * pre-render to determine margin sizes.
  25065. *
  25066. * @private
  25067. * @function Highcharts.Chart#setChartSize
  25068. *
  25069. * @param {boolean} skipAxes
  25070. *
  25071. * @return {void}
  25072. *
  25073. * @fires Highcharts.Chart#event:afterSetChartSize
  25074. */
  25075. setChartSize: function (skipAxes) {
  25076. 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;
  25077. /**
  25078. * The current left position of the plot area in pixels.
  25079. *
  25080. * @name Highcharts.Chart#plotLeft
  25081. * @type {number}
  25082. */
  25083. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  25084. /**
  25085. * The current top position of the plot area in pixels.
  25086. *
  25087. * @name Highcharts.Chart#plotTop
  25088. * @type {number}
  25089. */
  25090. chart.plotTop = plotTop = Math.round(chart.plotTop);
  25091. /**
  25092. * The current width of the plot area in pixels.
  25093. *
  25094. * @name Highcharts.Chart#plotWidth
  25095. * @type {number}
  25096. */
  25097. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  25098. /**
  25099. * The current height of the plot area in pixels.
  25100. *
  25101. * @name Highcharts.Chart#plotHeight
  25102. * @type {number}
  25103. */
  25104. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  25105. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  25106. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  25107. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  25108. // Set boxes used for alignment
  25109. chart.spacingBox = renderer.spacingBox = {
  25110. x: spacing[3],
  25111. y: spacing[0],
  25112. width: chartWidth - spacing[3] - spacing[1],
  25113. height: chartHeight - spacing[0] - spacing[2]
  25114. };
  25115. chart.plotBox = renderer.plotBox = {
  25116. x: plotLeft,
  25117. y: plotTop,
  25118. width: plotWidth,
  25119. height: plotHeight
  25120. };
  25121. plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
  25122. clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
  25123. clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  25124. chart.clipBox = {
  25125. x: clipX,
  25126. y: clipY,
  25127. width: Math.floor(chart.plotSizeX -
  25128. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  25129. clipX),
  25130. height: Math.max(0, Math.floor(chart.plotSizeY -
  25131. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  25132. clipY))
  25133. };
  25134. if (!skipAxes) {
  25135. chart.axes.forEach(function (axis) {
  25136. axis.setAxisSize();
  25137. axis.setAxisTranslation();
  25138. });
  25139. }
  25140. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  25141. },
  25142. /**
  25143. * Initial margins before auto size margins are applied.
  25144. *
  25145. * @private
  25146. * @function Highcharts.Chart#resetMargins
  25147. * @return {void}
  25148. */
  25149. resetMargins: function () {
  25150. fireEvent(this, 'resetMargins');
  25151. var chart = this, chartOptions = chart.options.chart;
  25152. // Create margin and spacing array
  25153. ['margin', 'spacing'].forEach(function splashArrays(target) {
  25154. var value = chartOptions[target], values = isObject(value) ? value : [value, value, value, value];
  25155. [
  25156. 'Top',
  25157. 'Right',
  25158. 'Bottom',
  25159. 'Left'
  25160. ].forEach(function (sideName, side) {
  25161. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  25162. });
  25163. });
  25164. // Set margin names like chart.plotTop, chart.plotLeft,
  25165. // chart.marginRight, chart.marginBottom.
  25166. marginNames.forEach(function (m, side) {
  25167. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  25168. });
  25169. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  25170. chart.clipOffset = [0, 0, 0, 0];
  25171. },
  25172. /**
  25173. * Internal function to draw or redraw the borders and backgrounds for chart
  25174. * and plot area.
  25175. *
  25176. * @private
  25177. * @function Highcharts.Chart#drawChartBox
  25178. * @return {void}
  25179. * @fires Highcharts.Chart#event:afterDrawChartBox
  25180. */
  25181. drawChartBox: function () {
  25182. 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';
  25183. // Chart area
  25184. if (!chartBackground) {
  25185. chart.chartBackground = chartBackground = renderer.rect()
  25186. .addClass('highcharts-background')
  25187. .add();
  25188. verb = 'attr';
  25189. }
  25190. if (!styledMode) {
  25191. // Presentational
  25192. chartBorderWidth = optionsChart.borderWidth || 0;
  25193. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  25194. bgAttr = {
  25195. fill: chartBackgroundColor || 'none'
  25196. };
  25197. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  25198. bgAttr.stroke = optionsChart.borderColor;
  25199. bgAttr['stroke-width'] = chartBorderWidth;
  25200. }
  25201. chartBackground
  25202. .attr(bgAttr)
  25203. .shadow(optionsChart.shadow);
  25204. }
  25205. else {
  25206. chartBorderWidth = mgn = chartBackground.strokeWidth();
  25207. }
  25208. chartBackground[verb]({
  25209. x: mgn / 2,
  25210. y: mgn / 2,
  25211. width: chartWidth - mgn - chartBorderWidth % 2,
  25212. height: chartHeight - mgn - chartBorderWidth % 2,
  25213. r: optionsChart.borderRadius
  25214. });
  25215. // Plot background
  25216. verb = 'animate';
  25217. if (!plotBackground) {
  25218. verb = 'attr';
  25219. chart.plotBackground = plotBackground = renderer.rect()
  25220. .addClass('highcharts-plot-background')
  25221. .add();
  25222. }
  25223. plotBackground[verb](plotBox);
  25224. if (!styledMode) {
  25225. // Presentational attributes for the background
  25226. plotBackground
  25227. .attr({
  25228. fill: plotBackgroundColor || 'none'
  25229. })
  25230. .shadow(optionsChart.plotShadow);
  25231. // Create the background image
  25232. if (plotBackgroundImage) {
  25233. if (!plotBGImage) {
  25234. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  25235. }
  25236. else {
  25237. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  25238. plotBGImage.attr('href', plotBackgroundImage);
  25239. }
  25240. plotBGImage.animate(plotBox);
  25241. }
  25242. }
  25243. }
  25244. // Plot clip
  25245. if (!clipRect) {
  25246. chart.clipRect = renderer.clipRect(clipBox);
  25247. }
  25248. else {
  25249. clipRect.animate({
  25250. width: clipBox.width,
  25251. height: clipBox.height
  25252. });
  25253. }
  25254. // Plot area border
  25255. verb = 'animate';
  25256. if (!plotBorder) {
  25257. verb = 'attr';
  25258. chart.plotBorder = plotBorder = renderer.rect()
  25259. .addClass('highcharts-plot-border')
  25260. .attr({
  25261. zIndex: 1 // Above the grid
  25262. })
  25263. .add();
  25264. }
  25265. if (!styledMode) {
  25266. // Presentational
  25267. plotBorder.attr({
  25268. stroke: optionsChart.plotBorderColor,
  25269. 'stroke-width': optionsChart.plotBorderWidth || 0,
  25270. fill: 'none'
  25271. });
  25272. }
  25273. plotBorder[verb](plotBorder.crisp({
  25274. x: plotLeft,
  25275. y: plotTop,
  25276. width: plotWidth,
  25277. height: plotHeight
  25278. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  25279. // reset
  25280. chart.isDirtyBox = false;
  25281. fireEvent(this, 'afterDrawChartBox');
  25282. },
  25283. /**
  25284. * Detect whether a certain chart property is needed based on inspecting its
  25285. * options and series. This mainly applies to the chart.inverted property,
  25286. * and in extensions to the chart.angular and chart.polar properties.
  25287. *
  25288. * @private
  25289. * @function Highcharts.Chart#propFromSeries
  25290. * @return {void}
  25291. */
  25292. propFromSeries: function () {
  25293. var chart = this, optionsChart = chart.options.chart, klass, seriesOptions = chart.options.series, i, value;
  25294. /**
  25295. * The flag is set to `true` if a series of the chart is inverted.
  25296. *
  25297. * @name Highcharts.Chart#inverted
  25298. * @type {boolean|undefined}
  25299. */
  25300. ['inverted', 'angular', 'polar'].forEach(function (key) {
  25301. // The default series type's class
  25302. klass = seriesTypes[(optionsChart.type ||
  25303. optionsChart.defaultSeriesType)];
  25304. // Get the value from available chart-wide properties
  25305. value =
  25306. // It is set in the options:
  25307. optionsChart[key] ||
  25308. // The default series class:
  25309. (klass && klass.prototype[key]);
  25310. // requires it
  25311. // 4. Check if any the chart's series require it
  25312. i = seriesOptions && seriesOptions.length;
  25313. while (!value && i--) {
  25314. klass = seriesTypes[seriesOptions[i].type];
  25315. if (klass && klass.prototype[key]) {
  25316. value = true;
  25317. }
  25318. }
  25319. // Set the chart property
  25320. chart[key] = value;
  25321. });
  25322. },
  25323. /**
  25324. * Internal function to link two or more series together, based on the
  25325. * `linkedTo` option. This is done from `Chart.render`, and after
  25326. * `Chart.addSeries` and `Series.remove`.
  25327. *
  25328. * @private
  25329. * @function Highcharts.Chart#linkSeries
  25330. * @return {void}
  25331. * @fires Highcharts.Chart#event:afterLinkSeries
  25332. */
  25333. linkSeries: function () {
  25334. var chart = this, chartSeries = chart.series;
  25335. // Reset links
  25336. chartSeries.forEach(function (series) {
  25337. series.linkedSeries.length = 0;
  25338. });
  25339. // Apply new links
  25340. chartSeries.forEach(function (series) {
  25341. var linkedTo = series.options.linkedTo;
  25342. if (isString(linkedTo)) {
  25343. if (linkedTo === ':previous') {
  25344. linkedTo = chart.series[series.index - 1];
  25345. }
  25346. else {
  25347. linkedTo = chart.get(linkedTo);
  25348. }
  25349. // #3341 avoid mutual linking
  25350. if (linkedTo && linkedTo.linkedParent !== series) {
  25351. linkedTo.linkedSeries.push(series);
  25352. series.linkedParent = linkedTo;
  25353. if (linkedTo.enabledDataSorting) {
  25354. series.setDataSortingOptions();
  25355. }
  25356. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  25357. }
  25358. }
  25359. });
  25360. fireEvent(this, 'afterLinkSeries');
  25361. },
  25362. /**
  25363. * Render series for the chart.
  25364. *
  25365. * @private
  25366. * @function Highcharts.Chart#renderSeries
  25367. * @return {void}
  25368. */
  25369. renderSeries: function () {
  25370. this.series.forEach(function (serie) {
  25371. serie.translate();
  25372. serie.render();
  25373. });
  25374. },
  25375. /**
  25376. * Render labels for the chart.
  25377. *
  25378. * @private
  25379. * @function Highcharts.Chart#renderLabels
  25380. * @return {void}
  25381. */
  25382. renderLabels: function () {
  25383. var chart = this, labels = chart.options.labels;
  25384. if (labels.items) {
  25385. labels.items.forEach(function (label) {
  25386. var style = extend(labels.style, label.style), x = pInt(style.left) + chart.plotLeft, y = pInt(style.top) + chart.plotTop + 12;
  25387. // delete to prevent rewriting in IE
  25388. delete style.left;
  25389. delete style.top;
  25390. chart.renderer.text(label.html, x, y)
  25391. .attr({ zIndex: 2 })
  25392. .css(style)
  25393. .add();
  25394. });
  25395. }
  25396. },
  25397. /**
  25398. * Render all graphics for the chart. Runs internally on initialization.
  25399. *
  25400. * @private
  25401. * @function Highcharts.Chart#render
  25402. * @return {void}
  25403. */
  25404. render: function () {
  25405. var chart = this, axes = chart.axes, colorAxis = chart.colorAxis, renderer = chart.renderer, options = chart.options, correction = 0, // correction for X axis labels
  25406. tempWidth, tempHeight, redoHorizontal, redoVertical, renderAxes = function (axes) {
  25407. axes.forEach(function (axis) {
  25408. if (axis.visible) {
  25409. axis.render();
  25410. }
  25411. });
  25412. };
  25413. // Title
  25414. chart.setTitle();
  25415. /**
  25416. * The overview of the chart's series.
  25417. *
  25418. * @name Highcharts.Chart#legend
  25419. * @type {Highcharts.Legend}
  25420. */
  25421. chart.legend = new Legend(chart, options.legend);
  25422. // Get stacks
  25423. if (chart.getStacks) {
  25424. chart.getStacks();
  25425. }
  25426. // Get chart margins
  25427. chart.getMargins(true);
  25428. chart.setChartSize();
  25429. // Record preliminary dimensions for later comparison
  25430. tempWidth = chart.plotWidth;
  25431. axes.some(function (axis) {
  25432. if (axis.horiz &&
  25433. axis.visible &&
  25434. axis.options.labels.enabled &&
  25435. axis.series.length) {
  25436. // 21 is the most common correction for X axis labels
  25437. correction = 21;
  25438. return true;
  25439. }
  25440. });
  25441. // use Math.max to prevent negative plotHeight
  25442. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  25443. tempHeight = chart.plotHeight;
  25444. // Get margins by pre-rendering axes
  25445. axes.forEach(function (axis) {
  25446. axis.setScale();
  25447. });
  25448. chart.getAxisMargins();
  25449. // If the plot area size has changed significantly, calculate tick
  25450. // positions again
  25451. redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  25452. // Height is more sensitive, use lower threshold
  25453. redoVertical = tempHeight / chart.plotHeight > 1.05;
  25454. if (redoHorizontal || redoVertical) {
  25455. axes.forEach(function (axis) {
  25456. if ((axis.horiz && redoHorizontal) ||
  25457. (!axis.horiz && redoVertical)) {
  25458. // update to reflect the new margins
  25459. axis.setTickInterval(true);
  25460. }
  25461. });
  25462. chart.getMargins(); // second pass to check for new labels
  25463. }
  25464. // Draw the borders and backgrounds
  25465. chart.drawChartBox();
  25466. // Axes
  25467. if (chart.hasCartesianSeries) {
  25468. renderAxes(axes);
  25469. }
  25470. else if (colorAxis && colorAxis.length) {
  25471. renderAxes(colorAxis);
  25472. }
  25473. // The series
  25474. if (!chart.seriesGroup) {
  25475. chart.seriesGroup = renderer.g('series-group')
  25476. .attr({ zIndex: 3 })
  25477. .add();
  25478. }
  25479. chart.renderSeries();
  25480. // Labels
  25481. chart.renderLabels();
  25482. // Credits
  25483. chart.addCredits();
  25484. // Handle responsiveness
  25485. if (chart.setResponsive) {
  25486. chart.setResponsive();
  25487. }
  25488. // Handle scaling
  25489. chart.updateContainerScaling();
  25490. // Set flag
  25491. chart.hasRendered = true;
  25492. },
  25493. /**
  25494. * Set a new credits label for the chart.
  25495. *
  25496. * @sample highcharts/credits/credits-update/
  25497. * Add and update credits
  25498. *
  25499. * @function Highcharts.Chart#addCredits
  25500. *
  25501. * @param {Highcharts.CreditsOptions} credits
  25502. * A configuration object for the new credits.
  25503. *
  25504. * @return {void}
  25505. */
  25506. addCredits: function (credits) {
  25507. var chart = this;
  25508. credits = merge(true, this.options.credits, credits);
  25509. if (credits.enabled && !this.credits) {
  25510. /**
  25511. * The chart's credits label. The label has an `update` method that
  25512. * allows setting new options as per the
  25513. * [credits options set](https://api.highcharts.com/highcharts/credits).
  25514. *
  25515. * @name Highcharts.Chart#credits
  25516. * @type {Highcharts.SVGElement}
  25517. */
  25518. this.credits = this.renderer.text(credits.text + (this.mapCredits || ''), 0, 0)
  25519. .addClass('highcharts-credits')
  25520. .on('click', function () {
  25521. if (credits.href) {
  25522. win.location.href = credits.href;
  25523. }
  25524. })
  25525. .attr({
  25526. align: credits.position.align,
  25527. zIndex: 8
  25528. });
  25529. if (!chart.styledMode) {
  25530. this.credits.css(credits.style);
  25531. }
  25532. this.credits
  25533. .add()
  25534. .align(credits.position);
  25535. // Dynamically update
  25536. this.credits.update = function (options) {
  25537. chart.credits = chart.credits.destroy();
  25538. chart.addCredits(options);
  25539. };
  25540. }
  25541. },
  25542. /**
  25543. * Handle scaling, #11329 - when there is scaling/transform on the container
  25544. * or on a parent element, we need to take this into account. We calculate
  25545. * the scaling once here and it is picked up where we need to use it
  25546. * (Pointer, Tooltip).
  25547. *
  25548. * @private
  25549. * @function Highcharts.Chart#updateContainerScaling
  25550. * @return {void}
  25551. */
  25552. updateContainerScaling: function () {
  25553. var container = this.container;
  25554. if (container.offsetWidth &&
  25555. container.offsetHeight &&
  25556. container.getBoundingClientRect) {
  25557. var bb = container.getBoundingClientRect(), scaleX = bb.width / container.offsetWidth, scaleY = bb.height / container.offsetHeight;
  25558. if (scaleX !== 1 || scaleY !== 1) {
  25559. this.containerScaling = { scaleX: scaleX, scaleY: scaleY };
  25560. }
  25561. else {
  25562. delete this.containerScaling;
  25563. }
  25564. }
  25565. },
  25566. /**
  25567. * Remove the chart and purge memory. This method is called internally
  25568. * before adding a second chart into the same container, as well as on
  25569. * window unload to prevent leaks.
  25570. *
  25571. * @sample highcharts/members/chart-destroy/
  25572. * Destroy the chart from a button
  25573. * @sample stock/members/chart-destroy/
  25574. * Destroy with Highstock
  25575. *
  25576. * @function Highcharts.Chart#destroy
  25577. *
  25578. * @return {void}
  25579. *
  25580. * @fires Highcharts.Chart#event:destroy
  25581. */
  25582. destroy: function () {
  25583. var chart = this, axes = chart.axes, series = chart.series, container = chart.container, i, parentNode = container && container.parentNode;
  25584. // fire the chart.destoy event
  25585. fireEvent(chart, 'destroy');
  25586. // Delete the chart from charts lookup array
  25587. if (chart.renderer.forExport) {
  25588. erase(charts, chart); // #6569
  25589. }
  25590. else {
  25591. charts[chart.index] = void 0;
  25592. }
  25593. H.chartCount--;
  25594. chart.renderTo.removeAttribute('data-highcharts-chart');
  25595. // remove events
  25596. removeEvent(chart);
  25597. // ==== Destroy collections:
  25598. // Destroy axes
  25599. i = axes.length;
  25600. while (i--) {
  25601. axes[i] = axes[i].destroy();
  25602. }
  25603. // Destroy scroller & scroller series before destroying base series
  25604. if (this.scroller && this.scroller.destroy) {
  25605. this.scroller.destroy();
  25606. }
  25607. // Destroy each series
  25608. i = series.length;
  25609. while (i--) {
  25610. series[i] = series[i].destroy();
  25611. }
  25612. // ==== Destroy chart properties:
  25613. [
  25614. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  25615. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  25616. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  25617. 'renderer'
  25618. ].forEach(function (name) {
  25619. var prop = chart[name];
  25620. if (prop && prop.destroy) {
  25621. chart[name] = prop.destroy();
  25622. }
  25623. });
  25624. // Remove container and all SVG, check container as it can break in IE
  25625. // when destroyed before finished loading
  25626. if (container) {
  25627. container.innerHTML = '';
  25628. removeEvent(container);
  25629. if (parentNode) {
  25630. discardElement(container);
  25631. }
  25632. }
  25633. // clean it all up
  25634. objectEach(chart, function (val, key) {
  25635. delete chart[key];
  25636. });
  25637. },
  25638. /**
  25639. * Prepare for first rendering after all data are loaded.
  25640. *
  25641. * @private
  25642. * @function Highcharts.Chart#firstRender
  25643. * @return {void}
  25644. *
  25645. * @fires Highcharts.Chart#event:beforeRender
  25646. */
  25647. firstRender: function () {
  25648. var chart = this, options = chart.options;
  25649. // Hook for oldIE to check whether the chart is ready to render
  25650. if (chart.isReadyToRender && !chart.isReadyToRender()) {
  25651. return;
  25652. }
  25653. // Create the container
  25654. chart.getContainer();
  25655. chart.resetMargins();
  25656. chart.setChartSize();
  25657. // Set the common chart properties (mainly invert) from the given series
  25658. chart.propFromSeries();
  25659. // get axes
  25660. chart.getAxes();
  25661. // Initialize the series
  25662. (isArray(options.series) ? options.series : []).forEach(
  25663. // #9680
  25664. function (serieOptions) {
  25665. chart.initSeries(serieOptions);
  25666. });
  25667. chart.linkSeries();
  25668. chart.setSeriesData();
  25669. // Run an event after axes and series are initialized, but before
  25670. // render. At this stage, the series data is indexed and cached in the
  25671. // xData and yData arrays, so we can access those before rendering. Used
  25672. // in Highstock.
  25673. fireEvent(chart, 'beforeRender');
  25674. // depends on inverted and on margins being set
  25675. if (Pointer) {
  25676. /**
  25677. * The Pointer that keeps track of mouse and touch interaction.
  25678. *
  25679. * @memberof Highcharts.Chart
  25680. * @name pointer
  25681. * @type {Highcharts.Pointer}
  25682. * @instance
  25683. */
  25684. chart.pointer = new Pointer(chart, options);
  25685. }
  25686. chart.render();
  25687. // Fire the load event if there are no external images
  25688. if (!chart.renderer.imgCount && chart.onload) {
  25689. chart.onload();
  25690. }
  25691. // If the chart was rendered outside the top container, put it back in
  25692. // (#3679)
  25693. chart.temporaryDisplay(true);
  25694. },
  25695. /**
  25696. * Internal function that runs on chart load, async if any images are loaded
  25697. * in the chart. Runs the callbacks and triggers the `load` and `render`
  25698. * events.
  25699. *
  25700. * @private
  25701. * @function Highcharts.Chart#onload
  25702. * @return {void}
  25703. * @fires Highcharts.Chart#event:load
  25704. * @fires Highcharts.Chart#event:render
  25705. */
  25706. onload: function () {
  25707. // Run callbacks, first the ones registered by modules, then user's one
  25708. this.callbacks.concat([this.callback]).forEach(function (fn) {
  25709. // Chart destroyed in its own callback (#3600)
  25710. if (fn && typeof this.index !== 'undefined') {
  25711. fn.apply(this, [this]);
  25712. }
  25713. }, this);
  25714. fireEvent(this, 'load');
  25715. fireEvent(this, 'render');
  25716. // Set up auto resize, check for not destroyed (#6068)
  25717. if (defined(this.index)) {
  25718. this.setReflow(this.options.chart.reflow);
  25719. }
  25720. // Don't run again
  25721. this.onload = null;
  25722. }
  25723. }); // end Chart
  25724. });
  25725. _registerModule(_modules, 'parts/ScrollablePlotArea.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  25726. /* *
  25727. *
  25728. * (c) 2010-2019 Torstein Honsi
  25729. *
  25730. * License: www.highcharts.com/license
  25731. *
  25732. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  25733. *
  25734. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  25735. * horizontally on mobile devices. Supports left and right side axes.
  25736. */
  25737. /*
  25738. WIP on vertical scrollable plot area (#9378). To do:
  25739. - Bottom axis positioning
  25740. - Test with Gantt
  25741. - Look for size optimizing the code
  25742. - API and demos
  25743. */
  25744. var pick = U.pick;
  25745. var addEvent = H.addEvent, Chart = H.Chart;
  25746. /**
  25747. * Options for a scrollable plot area. This feature provides a minimum size for
  25748. * the plot area of the chart. If the size gets smaller than this, typically
  25749. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  25750. * provides smooth scrolling for the contents of the plot area, whereas the
  25751. * title, legend and unaffected axes are fixed.
  25752. *
  25753. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  25754. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  25755. * option is set.
  25756. *
  25757. * @sample highcharts/chart/scrollable-plotarea
  25758. * Scrollable plot area
  25759. * @sample highcharts/chart/scrollable-plotarea-vertical
  25760. * Vertically scrollable plot area
  25761. * @sample {gantt} highcharts/chart/scrollable-plotarea-vertical
  25762. * Gantt chart with vertically scrollable plot area
  25763. *
  25764. * @since 6.1.0
  25765. * @product highcharts gantt
  25766. * @apioption chart.scrollablePlotArea
  25767. */
  25768. /**
  25769. * The minimum height for the plot area. If it gets smaller than this, the plot
  25770. * area will become scrollable.
  25771. *
  25772. * @type {number}
  25773. * @apioption chart.scrollablePlotArea.minHeight
  25774. */
  25775. /**
  25776. * The minimum width for the plot area. If it gets smaller than this, the plot
  25777. * area will become scrollable.
  25778. *
  25779. * @type {number}
  25780. * @apioption chart.scrollablePlotArea.minWidth
  25781. */
  25782. /**
  25783. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  25784. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  25785. * Typically we would use 1 if the chart has right aligned Y axes.
  25786. *
  25787. * @type {number}
  25788. * @apioption chart.scrollablePlotArea.scrollPositionX
  25789. */
  25790. /**
  25791. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  25792. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  25793. *
  25794. * @type {number}
  25795. * @apioption chart.scrollablePlotArea.scrollPositionY
  25796. */
  25797. /**
  25798. * The opacity of mask applied on one of the sides of the plot
  25799. * area.
  25800. *
  25801. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  25802. * Disabled opacity for the mask
  25803. *
  25804. * @type {number}
  25805. * @default 0.85
  25806. * @since 7.1.1
  25807. * @apioption chart.scrollablePlotArea.opacity
  25808. */
  25809. ''; // detach API doclets
  25810. /* eslint-disable no-invalid-this, valid-jsdoc */
  25811. addEvent(Chart, 'afterSetChartSize', function (e) {
  25812. var scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
  25813. if (!this.renderer.forExport) {
  25814. // The amount of pixels to scroll, the difference between chart
  25815. // width and scrollable width
  25816. if (scrollableMinWidth) {
  25817. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  25818. if (scrollablePixelsX) {
  25819. this.plotWidth += scrollablePixelsX;
  25820. if (this.inverted) {
  25821. this.clipBox.height += scrollablePixelsX;
  25822. this.plotBox.height += scrollablePixelsX;
  25823. }
  25824. else {
  25825. this.clipBox.width += scrollablePixelsX;
  25826. this.plotBox.width += scrollablePixelsX;
  25827. }
  25828. corrections = {
  25829. // Corrections for right side
  25830. 1: { name: 'right', value: scrollablePixelsX }
  25831. };
  25832. }
  25833. // Currently we can only do either X or Y
  25834. }
  25835. else if (scrollableMinHeight) {
  25836. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  25837. if (scrollablePixelsY) {
  25838. this.plotHeight += scrollablePixelsY;
  25839. if (this.inverted) {
  25840. this.clipBox.width += scrollablePixelsY;
  25841. this.plotBox.width += scrollablePixelsY;
  25842. }
  25843. else {
  25844. this.clipBox.height += scrollablePixelsY;
  25845. this.plotBox.height += scrollablePixelsY;
  25846. }
  25847. corrections = {
  25848. 2: { name: 'bottom', value: scrollablePixelsY }
  25849. };
  25850. }
  25851. }
  25852. if (corrections && !e.skipAxes) {
  25853. this.axes.forEach(function (axis) {
  25854. // For right and bottom axes, only fix the plot line length
  25855. if (corrections[axis.side]) {
  25856. // Get the plot lines right in getPlotLinePath,
  25857. // temporarily set it to the adjusted plot width.
  25858. axis.getPlotLinePath = function () {
  25859. var marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
  25860. // axis.right or axis.bottom
  25861. margin = this[marginName], path;
  25862. // Temporarily adjust
  25863. this[marginName] = margin - correctionValue;
  25864. path = H.Axis.prototype.getPlotLinePath.apply(this, arguments);
  25865. // Reset
  25866. this[marginName] = margin;
  25867. return path;
  25868. };
  25869. }
  25870. else {
  25871. // Apply the corrected plotWidth
  25872. axis.setAxisSize();
  25873. axis.setAxisTranslation();
  25874. }
  25875. });
  25876. }
  25877. }
  25878. });
  25879. addEvent(Chart, 'render', function () {
  25880. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  25881. if (this.setUpScrolling) {
  25882. this.setUpScrolling();
  25883. }
  25884. this.applyFixed();
  25885. }
  25886. else if (this.fixedDiv) { // Has been in scrollable mode
  25887. this.applyFixed();
  25888. }
  25889. });
  25890. /**
  25891. * @private
  25892. * @function Highcharts.Chart#setUpScrolling
  25893. * @return {void}
  25894. */
  25895. Chart.prototype.setUpScrolling = function () {
  25896. var attribs = {
  25897. WebkitOverflowScrolling: 'touch',
  25898. overflowX: 'hidden',
  25899. overflowY: 'hidden'
  25900. };
  25901. if (this.scrollablePixelsX) {
  25902. attribs.overflowX = 'auto';
  25903. }
  25904. if (this.scrollablePixelsY) {
  25905. attribs.overflowY = 'auto';
  25906. }
  25907. // Add the necessary divs to provide scrolling
  25908. this.scrollingContainer = H.createElement('div', {
  25909. 'className': 'highcharts-scrolling'
  25910. }, attribs, this.renderTo);
  25911. this.innerContainer = H.createElement('div', {
  25912. 'className': 'highcharts-inner-container'
  25913. }, null, this.scrollingContainer);
  25914. // Now move the container inside
  25915. this.innerContainer.appendChild(this.container);
  25916. // Don't run again
  25917. this.setUpScrolling = null;
  25918. };
  25919. /**
  25920. * These elements are moved over to the fixed renderer and stay fixed when the
  25921. * user scrolls the chart
  25922. * @private
  25923. */
  25924. Chart.prototype.moveFixedElements = function () {
  25925. var container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
  25926. '.highcharts-contextbutton',
  25927. '.highcharts-credits',
  25928. '.highcharts-legend',
  25929. '.highcharts-legend-checkbox',
  25930. '.highcharts-navigator-series',
  25931. '.highcharts-navigator-xaxis',
  25932. '.highcharts-navigator-yaxis',
  25933. '.highcharts-navigator',
  25934. '.highcharts-reset-zoom',
  25935. '.highcharts-scrollbar',
  25936. '.highcharts-subtitle',
  25937. '.highcharts-title'
  25938. ], axisClass;
  25939. if (this.scrollablePixelsX && !this.inverted) {
  25940. axisClass = '.highcharts-yaxis';
  25941. }
  25942. else if (this.scrollablePixelsX && this.inverted) {
  25943. axisClass = '.highcharts-xaxis';
  25944. }
  25945. else if (this.scrollablePixelsY && !this.inverted) {
  25946. axisClass = '.highcharts-xaxis';
  25947. }
  25948. else if (this.scrollablePixelsY && this.inverted) {
  25949. axisClass = '.highcharts-yaxis';
  25950. }
  25951. fixedSelectors.push(axisClass, axisClass + '-labels');
  25952. fixedSelectors.forEach(function (className) {
  25953. [].forEach.call(container.querySelectorAll(className), function (elem) {
  25954. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  25955. fixedRenderer.box :
  25956. fixedRenderer.box.parentNode).appendChild(elem);
  25957. elem.style.pointerEvents = 'auto';
  25958. });
  25959. });
  25960. };
  25961. /**
  25962. * @private
  25963. * @function Highcharts.Chart#applyFixed
  25964. * @return {void}
  25965. */
  25966. Chart.prototype.applyFixed = function () {
  25967. var fixedRenderer, scrollableWidth, scrollableHeight, firstTime = !this.fixedDiv, scrollableOptions = this.options.chart.scrollablePlotArea;
  25968. // First render
  25969. if (firstTime) {
  25970. this.fixedDiv = H.createElement('div', {
  25971. className: 'highcharts-fixed'
  25972. }, {
  25973. position: 'absolute',
  25974. overflow: 'hidden',
  25975. pointerEvents: 'none',
  25976. zIndex: 2
  25977. }, null, true);
  25978. this.renderTo.insertBefore(this.fixedDiv, this.renderTo.firstChild);
  25979. this.renderTo.style.overflow = 'visible';
  25980. this.fixedRenderer = fixedRenderer = new H.Renderer(this.fixedDiv, this.chartWidth, this.chartHeight);
  25981. // Mask
  25982. this.scrollableMask = fixedRenderer
  25983. .path()
  25984. .attr({
  25985. fill: this.options.chart.backgroundColor || '#fff',
  25986. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  25987. zIndex: -1
  25988. })
  25989. .addClass('highcharts-scrollable-mask')
  25990. .add();
  25991. this.moveFixedElements();
  25992. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  25993. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  25994. }
  25995. else {
  25996. // Set the size of the fixed renderer to the visible width
  25997. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  25998. }
  25999. // Increase the size of the scrollable renderer and background
  26000. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  26001. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  26002. H.stop(this.container);
  26003. this.container.style.width = scrollableWidth + 'px';
  26004. this.container.style.height = scrollableHeight + 'px';
  26005. this.renderer.boxWrapper.attr({
  26006. width: scrollableWidth,
  26007. height: scrollableHeight,
  26008. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  26009. });
  26010. this.chartBackground.attr({
  26011. width: scrollableWidth,
  26012. height: scrollableHeight
  26013. });
  26014. if (this.scrollablePixelsY) {
  26015. this.scrollingContainer.style.height = this.chartHeight + 'px';
  26016. }
  26017. // Set scroll position
  26018. if (firstTime) {
  26019. if (scrollableOptions.scrollPositionX) {
  26020. this.scrollingContainer.scrollLeft =
  26021. this.scrollablePixelsX *
  26022. scrollableOptions.scrollPositionX;
  26023. }
  26024. if (scrollableOptions.scrollPositionY) {
  26025. this.scrollingContainer.scrollTop =
  26026. this.scrollablePixelsY *
  26027. scrollableOptions.scrollPositionY;
  26028. }
  26029. }
  26030. // Mask behind the left and right side
  26031. 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 -
  26032. (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
  26033. (this.scrollablePixelsY || 0), d;
  26034. if (this.scrollablePixelsX) {
  26035. d = [
  26036. // Left side
  26037. 'M', 0, maskTop,
  26038. 'L', this.plotLeft - 1, maskTop,
  26039. 'L', this.plotLeft - 1, maskBottom,
  26040. 'L', 0, maskBottom,
  26041. 'Z',
  26042. // Right side
  26043. 'M', maskPlotRight, maskTop,
  26044. 'L', this.chartWidth, maskTop,
  26045. 'L', this.chartWidth, maskBottom,
  26046. 'L', maskPlotRight, maskBottom,
  26047. 'Z'
  26048. ];
  26049. }
  26050. else if (this.scrollablePixelsY) {
  26051. d = [
  26052. // Top side
  26053. 'M', maskLeft, 0,
  26054. 'L', maskLeft, this.plotTop - 1,
  26055. 'L', maskRight, this.plotTop - 1,
  26056. 'L', maskRight, 0,
  26057. 'Z',
  26058. // Bottom side
  26059. 'M', maskLeft, maskPlotBottom,
  26060. 'L', maskLeft, this.chartHeight,
  26061. 'L', maskRight, this.chartHeight,
  26062. 'L', maskRight, maskPlotBottom,
  26063. 'Z'
  26064. ];
  26065. }
  26066. else {
  26067. d = ['M', 0, 0];
  26068. }
  26069. if (this.redrawTrigger !== 'adjustHeight') {
  26070. this.scrollableMask.attr({
  26071. d: d
  26072. });
  26073. }
  26074. };
  26075. });
  26076. _registerModule(_modules, 'parts/Point.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  26077. /* *
  26078. *
  26079. * (c) 2010-2019 Torstein Honsi
  26080. *
  26081. * License: www.highcharts.com/license
  26082. *
  26083. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  26084. *
  26085. * */
  26086. /**
  26087. * Function callback when a series point is clicked. Return false to cancel the
  26088. * action.
  26089. *
  26090. * @callback Highcharts.PointClickCallbackFunction
  26091. *
  26092. * @param {Highcharts.Point} this
  26093. * The point where the event occured.
  26094. *
  26095. * @param {Highcharts.PointClickEventObject} event
  26096. * Event arguments.
  26097. */
  26098. /**
  26099. * Common information for a click event on a series point.
  26100. *
  26101. * @interface Highcharts.PointClickEventObject
  26102. * @extends Highcharts.PointerEventObject
  26103. */ /**
  26104. * Clicked point.
  26105. * @name Highcharts.PointClickEventObject#point
  26106. * @type {Highcharts.Point}
  26107. */
  26108. /**
  26109. * Configuration hash for the data label and tooltip formatters.
  26110. *
  26111. * @interface Highcharts.PointLabelObject
  26112. */ /**
  26113. * The point's current color.
  26114. * @name Highcharts.PointLabelObject#color
  26115. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  26116. */ /**
  26117. * The point's current color index, used in styled mode instead of `color`. The
  26118. * color index is inserted in class names used for styling.
  26119. * @name Highcharts.PointLabelObject#colorIndex
  26120. * @type {number}
  26121. */ /**
  26122. * The name of the related point.
  26123. * @name Highcharts.PointLabelObject#key
  26124. * @type {string|undefined}
  26125. */ /**
  26126. * The percentage for related points in a stacked series or pies.
  26127. * @name Highcharts.PointLabelObject#percentage
  26128. * @type {number}
  26129. */ /**
  26130. * The related point.
  26131. * @name Highcharts.PointLabelObject#point
  26132. * @type {Highcharts.Point}
  26133. */ /**
  26134. * The related series.
  26135. * @name Highcharts.PointLabelObject#series
  26136. * @type {Highcharts.Series}
  26137. */ /**
  26138. * The total of values in either a stack for stacked series, or a pie in a pie
  26139. * series.
  26140. * @name Highcharts.PointLabelObject#total
  26141. * @type {number|undefined}
  26142. */ /**
  26143. * For categorized axes this property holds the category name for the point. For
  26144. * other axes it holds the X value.
  26145. * @name Highcharts.PointLabelObject#x
  26146. * @type {number|string|undefined}
  26147. */ /**
  26148. * The y value of the point.
  26149. * @name Highcharts.PointLabelObject#y
  26150. * @type {number|undefined}
  26151. */
  26152. /**
  26153. * Gets fired when the mouse leaves the area close to the point.
  26154. *
  26155. * @callback Highcharts.PointMouseOutCallbackFunction
  26156. *
  26157. * @param {Highcharts.Point} this
  26158. * Point where the event occured.
  26159. *
  26160. * @param {global.PointerEvent} event
  26161. * Event that occured.
  26162. */
  26163. /**
  26164. * Gets fired when the mouse enters the area close to the point.
  26165. *
  26166. * @callback Highcharts.PointMouseOverCallbackFunction
  26167. *
  26168. * @param {Highcharts.Point} this
  26169. * Point where the event occured.
  26170. *
  26171. * @param {global.Event} event
  26172. * Event that occured.
  26173. */
  26174. /**
  26175. * The generic point options for all series.
  26176. *
  26177. * In TypeScript you have to extend `PointOptionsObject` with an additional
  26178. * declaration to allow custom data options:
  26179. *
  26180. * ```
  26181. * declare interface PointOptionsObject {
  26182. * customProperty: string;
  26183. * }
  26184. * ```
  26185. *
  26186. * @interface Highcharts.PointOptionsObject
  26187. */
  26188. /**
  26189. * Possible option types for a data point.
  26190. *
  26191. * @typedef {number|string|Array<(number|string)>|Highcharts.PointOptionsObject|null} Highcharts.PointOptionsType
  26192. */
  26193. /**
  26194. * Gets fired when the point is removed using the `.remove()` method.
  26195. *
  26196. * @callback Highcharts.PointRemoveCallbackFunction
  26197. *
  26198. * @param {Highcharts.Point} this
  26199. * Point where the event occured.
  26200. *
  26201. * @param {global.Event} event
  26202. * Event that occured.
  26203. */
  26204. /**
  26205. * Possible key values for the point state options.
  26206. *
  26207. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  26208. */
  26209. /**
  26210. * Gets fired when the point is updated programmatically through the `.update()`
  26211. * method.
  26212. *
  26213. * @callback Highcharts.PointUpdateCallbackFunction
  26214. *
  26215. * @param {Highcharts.Point} this
  26216. * Point where the event occured.
  26217. *
  26218. * @param {Highcharts.PointUpdateEventObject} event
  26219. * Event that occured.
  26220. */
  26221. /**
  26222. * Information about the update event.
  26223. *
  26224. * @interface Highcharts.PointUpdateEventObject
  26225. * @extends global.Event
  26226. */ /**
  26227. * Options data of the update event.
  26228. * @name Highcharts.PointUpdateEventObject#options
  26229. * @type {Highcharts.PointOptionsType}
  26230. */
  26231. var animObject = U.animObject, defined = U.defined, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, syncTimeout = U.syncTimeout, pick = U.pick;
  26232. var Point, H = Highcharts, fireEvent = H.fireEvent, format = H.format, uniqueKey = H.uniqueKey, removeEvent = H.removeEvent;
  26233. /* eslint-disable no-invalid-this, valid-jsdoc */
  26234. /**
  26235. * The Point object. The point objects are generated from the `series.data`
  26236. * configuration objects or raw numbers. They can be accessed from the
  26237. * `Series.points` array. Other ways to instantiate points are through {@link
  26238. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  26239. *
  26240. * @class
  26241. * @name Highcharts.Point
  26242. */
  26243. Highcharts.Point = Point = function () { };
  26244. Highcharts.Point.prototype = {
  26245. /**
  26246. * Initialize the point. Called internally based on the `series.data`
  26247. * option.
  26248. *
  26249. * @function Highcharts.Point#init
  26250. *
  26251. * @param {Highcharts.Series} series
  26252. * The series object containing this point.
  26253. *
  26254. * @param {Highcharts.PointOptionsType} options
  26255. * The data in either number, array or object format.
  26256. *
  26257. * @param {number} [x]
  26258. * Optionally, the X value of the point.
  26259. *
  26260. * @return {Highcharts.Point}
  26261. * The Point instance.
  26262. *
  26263. * @fires Highcharts.Point#event:afterInit
  26264. */
  26265. init: function (series, options, x) {
  26266. /**
  26267. * The series object associated with the point.
  26268. *
  26269. * @name Highcharts.Point#series
  26270. * @type {Highcharts.Series}
  26271. */
  26272. this.series = series;
  26273. this.applyOptions(options, x);
  26274. // Add a unique ID to the point if none is assigned
  26275. this.id = defined(this.id) ? this.id : uniqueKey();
  26276. this.resolveColor();
  26277. series.chart.pointCount++;
  26278. fireEvent(this, 'afterInit');
  26279. return this;
  26280. },
  26281. /**
  26282. * @private
  26283. * @function Highcharts.Point#resolveColor
  26284. * @return {void}
  26285. */
  26286. resolveColor: function () {
  26287. var series = this.series, colors, optionsChart = series.chart.options.chart, colorCount = optionsChart.colorCount, styledMode = series.chart.styledMode, colorIndex;
  26288. /**
  26289. * The point's current color.
  26290. *
  26291. * @name Highcharts.Point#color
  26292. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  26293. */
  26294. if (!styledMode && !this.options.color) {
  26295. this.color = series.color; // #3445
  26296. }
  26297. if (series.options.colorByPoint) {
  26298. if (!styledMode) {
  26299. colors = series.options.colors || series.chart.options.colors;
  26300. this.color = this.color || colors[series.colorCounter];
  26301. colorCount = colors.length;
  26302. }
  26303. colorIndex = series.colorCounter;
  26304. series.colorCounter++;
  26305. // loop back to zero
  26306. if (series.colorCounter === colorCount) {
  26307. series.colorCounter = 0;
  26308. }
  26309. }
  26310. else {
  26311. colorIndex = series.colorIndex;
  26312. }
  26313. /**
  26314. * The point's current color index, used in styled mode instead of
  26315. * `color`. The color index is inserted in class names used for styling.
  26316. *
  26317. * @name Highcharts.Point#colorIndex
  26318. * @type {number}
  26319. */
  26320. this.colorIndex = pick(this.colorIndex, colorIndex);
  26321. },
  26322. /**
  26323. * Apply the options containing the x and y data and possible some extra
  26324. * properties. Called on point init or from point.update.
  26325. *
  26326. * @private
  26327. * @function Highcharts.Point#applyOptions
  26328. *
  26329. * @param {Highcharts.PointOptionsType} options
  26330. * The point options as defined in series.data.
  26331. *
  26332. * @param {number} [x]
  26333. * Optionally, the x value.
  26334. *
  26335. * @return {Highcharts.Point}
  26336. * The Point instance.
  26337. */
  26338. applyOptions: function (options, x) {
  26339. var point = this, series = point.series, pointValKey = series.options.pointValKey || series.pointValKey;
  26340. options = Point.prototype.optionsToObject.call(this, options);
  26341. // copy options directly to point
  26342. extend(point, options);
  26343. /**
  26344. * The point's options as applied in the initial configuration, or
  26345. * extended through `Point.update`.
  26346. *
  26347. * In TypeScript you have to extend `PointOptionsObject` via an
  26348. * additional interface to allow custom data options:
  26349. *
  26350. * ```
  26351. * declare interface PointOptionsObject {
  26352. * customProperty: string;
  26353. * }
  26354. * ```
  26355. *
  26356. * @name Highcharts.Point#options
  26357. * @type {Highcharts.PointOptionsObject}
  26358. */
  26359. point.options = point.options ?
  26360. extend(point.options, options) :
  26361. options;
  26362. // Since options are copied into the Point instance, some accidental
  26363. // options must be shielded (#5681)
  26364. if (options.group) {
  26365. delete point.group;
  26366. }
  26367. if (options.dataLabels) {
  26368. delete point.dataLabels;
  26369. }
  26370. /**
  26371. * The y value of the point.
  26372. * @name Highcharts.Point#y
  26373. * @type {number|undefined}
  26374. */
  26375. // For higher dimension series types. For instance, for ranges, point.y
  26376. // is mapped to point.low.
  26377. if (pointValKey) {
  26378. point.y = point[pointValKey];
  26379. }
  26380. point.isNull = pick(point.isValid && !point.isValid(), point.x === null || !isNumber(point.y)); // #3571, check for NaN
  26381. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  26382. // The point is initially selected by options (#5777)
  26383. if (point.selected) {
  26384. point.state = 'select';
  26385. }
  26386. /**
  26387. * The x value of the point.
  26388. * @name Highcharts.Point#x
  26389. * @type {number}
  26390. */
  26391. // If no x is set by now, get auto incremented value. All points must
  26392. // have an x value, however the y value can be null to create a gap in
  26393. // the series
  26394. if ('name' in point &&
  26395. typeof x === 'undefined' &&
  26396. series.xAxis &&
  26397. series.xAxis.hasNames) {
  26398. point.x = series.xAxis.nameToX(point);
  26399. }
  26400. if (typeof point.x === 'undefined' && series) {
  26401. if (typeof x === 'undefined') {
  26402. point.x = series.autoIncrement(point);
  26403. }
  26404. else {
  26405. point.x = x;
  26406. }
  26407. }
  26408. return point;
  26409. },
  26410. /**
  26411. * Set a value in an object, on the property defined by key. The key
  26412. * supports nested properties using dot notation. The function modifies the
  26413. * input object and does not make a copy.
  26414. *
  26415. * @function Highcharts.Point#setNestedProperty<T>
  26416. *
  26417. * @param {T} object
  26418. * The object to set the value on.
  26419. *
  26420. * @param {*} value
  26421. * The value to set.
  26422. *
  26423. * @param {string} key
  26424. * Key to the property to set.
  26425. *
  26426. * @return {T}
  26427. * The modified object.
  26428. */
  26429. setNestedProperty: function (object, value, key) {
  26430. var nestedKeys = key.split('.');
  26431. nestedKeys.reduce(function (result, key, i, arr) {
  26432. var isLastKey = arr.length - 1 === i;
  26433. result[key] = (isLastKey ?
  26434. value :
  26435. isObject(result[key], true) ?
  26436. result[key] :
  26437. {});
  26438. return result[key];
  26439. }, object);
  26440. return object;
  26441. },
  26442. /**
  26443. * Transform number or array configs into objects. Also called for object
  26444. * configs. Used internally to unify the different configuration formats for
  26445. * points. For example, a simple number `10` in a line series will be
  26446. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  26447. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  26448. *
  26449. * @function Highcharts.Point#optionsToObject
  26450. *
  26451. * @param {Highcharts.PointOptionsType} options
  26452. * The input option.
  26453. *
  26454. * @return {Highcharts.Dictionary<*>}
  26455. * Transformed options.
  26456. */
  26457. optionsToObject: function (options) {
  26458. var ret = {}, series = this.series, keys = series.options.keys, pointArrayMap = keys || series.pointArrayMap || ['y'], valueCount = pointArrayMap.length, firstItemType, i = 0, j = 0;
  26459. if (isNumber(options) || options === null) {
  26460. ret[pointArrayMap[0]] = options;
  26461. }
  26462. else if (isArray(options)) {
  26463. // with leading x value
  26464. if (!keys && options.length > valueCount) {
  26465. firstItemType = typeof options[0];
  26466. if (firstItemType === 'string') {
  26467. ret.name = options[0];
  26468. }
  26469. else if (firstItemType === 'number') {
  26470. ret.x = options[0];
  26471. }
  26472. i++;
  26473. }
  26474. while (j < valueCount) {
  26475. // Skip undefined positions for keys
  26476. if (!keys || typeof options[i] !== 'undefined') {
  26477. if (pointArrayMap[j].indexOf('.') > 0) {
  26478. // Handle nested keys, e.g. ['color.pattern.image']
  26479. // Avoid function call unless necessary.
  26480. H.Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  26481. }
  26482. else {
  26483. ret[pointArrayMap[j]] = options[i];
  26484. }
  26485. }
  26486. i++;
  26487. j++;
  26488. }
  26489. }
  26490. else if (typeof options === 'object') {
  26491. ret = options;
  26492. // This is the fastest way to detect if there are individual point
  26493. // dataLabels that need to be considered in drawDataLabels. These
  26494. // can only occur in object configs.
  26495. if (options.dataLabels) {
  26496. series._hasPointLabels = true;
  26497. }
  26498. // Same approach as above for markers
  26499. if (options.marker) {
  26500. series._hasPointMarkers = true;
  26501. }
  26502. }
  26503. return ret;
  26504. },
  26505. /**
  26506. * Get the CSS class names for individual points. Used internally where the
  26507. * returned value is set on every point.
  26508. *
  26509. * @function Highcharts.Point#getClassName
  26510. *
  26511. * @return {string}
  26512. * The class names.
  26513. */
  26514. getClassName: function () {
  26515. return 'highcharts-point' +
  26516. (this.selected ? ' highcharts-point-select' : '') +
  26517. (this.negative ? ' highcharts-negative' : '') +
  26518. (this.isNull ? ' highcharts-null-point' : '') +
  26519. (typeof this.colorIndex !== 'undefined' ?
  26520. ' highcharts-color-' + this.colorIndex : '') +
  26521. (this.options.className ? ' ' + this.options.className : '') +
  26522. (this.zone && this.zone.className ? ' ' +
  26523. this.zone.className.replace('highcharts-negative', '') : '');
  26524. },
  26525. /**
  26526. * In a series with `zones`, return the zone that the point belongs to.
  26527. *
  26528. * @function Highcharts.Point#getZone
  26529. *
  26530. * @return {Highcharts.SeriesZonesOptionsObject}
  26531. * The zone item.
  26532. */
  26533. getZone: function () {
  26534. var series = this.series, zones = series.zones, zoneAxis = series.zoneAxis || 'y', i = 0, zone;
  26535. zone = zones[i];
  26536. while (this[zoneAxis] >= zone.value) {
  26537. zone = zones[++i];
  26538. }
  26539. // For resetting or reusing the point (#8100)
  26540. if (!this.nonZonedColor) {
  26541. this.nonZonedColor = this.color;
  26542. }
  26543. if (zone && zone.color && !this.options.color) {
  26544. this.color = zone.color;
  26545. }
  26546. else {
  26547. this.color = this.nonZonedColor;
  26548. }
  26549. return zone;
  26550. },
  26551. /**
  26552. * Utility to check if point has new shape type. Used in column series and
  26553. * all others that are based on column series.
  26554. *
  26555. * @return boolean|undefined
  26556. */
  26557. hasNewShapeType: function () {
  26558. var oldShapeType = this.graphic &&
  26559. (this.graphic.symbolName || this.graphic.element.nodeName);
  26560. return oldShapeType !== this.shapeType;
  26561. },
  26562. /**
  26563. * Destroy a point to clear memory. Its reference still stays in
  26564. * `series.data`.
  26565. *
  26566. * @private
  26567. * @function Highcharts.Point#destroy
  26568. * @return {void}
  26569. */
  26570. destroy: function () {
  26571. 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;
  26572. /**
  26573. * Allow to call after animation.
  26574. * @private
  26575. */
  26576. function destroyPoint() {
  26577. if (hoverPoints) {
  26578. point.setState();
  26579. erase(hoverPoints, point);
  26580. if (!hoverPoints.length) {
  26581. chart.hoverPoints = null;
  26582. }
  26583. }
  26584. if (point === chart.hoverPoint) {
  26585. point.onMouseOut();
  26586. }
  26587. // Remove all events and elements
  26588. if (point.graphic || point.dataLabel || point.dataLabels) {
  26589. removeEvent(point);
  26590. point.destroyElements();
  26591. }
  26592. for (prop in point) { // eslint-disable-line guard-for-in
  26593. point[prop] = null;
  26594. }
  26595. }
  26596. // Remove properties after animation
  26597. if (!dataSorting || !dataSorting.enabled) {
  26598. destroyPoint();
  26599. }
  26600. else {
  26601. this.animateBeforeDestroy();
  26602. syncTimeout(destroyPoint, animation.duration);
  26603. }
  26604. chart.pointCount--;
  26605. if (point.legendItem) { // pies have legend items
  26606. chart.legend.destroyItem(point);
  26607. }
  26608. },
  26609. /**
  26610. * Animate SVG elements associated with the point.
  26611. *
  26612. * @private
  26613. * @function Highcharts.Point#animateBeforeDestroy
  26614. * @return {void}
  26615. */
  26616. animateBeforeDestroy: function () {
  26617. var point = this, animateParams = { x: point.startXPos, opacity: 0 }, isDataLabel, graphicalProps = point.getGraphicalProps();
  26618. graphicalProps.singular.forEach(function (prop) {
  26619. isDataLabel = prop === 'dataLabel';
  26620. point[prop] = point[prop].animate(isDataLabel ? {
  26621. x: point[prop].startXPos,
  26622. y: point[prop].startYPos,
  26623. opacity: 0
  26624. } : animateParams);
  26625. });
  26626. graphicalProps.plural.forEach(function (plural) {
  26627. point[plural].forEach(function (item) {
  26628. if (item.element) {
  26629. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  26630. x: item.startXPos,
  26631. y: item.startYPos
  26632. } : {})));
  26633. }
  26634. });
  26635. });
  26636. },
  26637. /**
  26638. * Destroy SVG elements associated with the point.
  26639. *
  26640. * @private
  26641. * @function Highcharts.Point#destroyElements
  26642. * @param {Highcharts.Dictionary<number>} [kinds]
  26643. * @return {void}
  26644. */
  26645. destroyElements: function (kinds) {
  26646. var point = this, props = point.getGraphicalProps(kinds);
  26647. props.singular.forEach(function (prop) {
  26648. point[prop] = point[prop].destroy();
  26649. });
  26650. props.plural.forEach(function (plural) {
  26651. point[plural].forEach(function (item) {
  26652. if (item.element) {
  26653. item.destroy();
  26654. }
  26655. });
  26656. delete point[plural];
  26657. });
  26658. },
  26659. /**
  26660. * Get props of all existing graphical point elements.
  26661. *
  26662. * @private
  26663. * @function Highcharts.Point#getGraphicalProps
  26664. * @param {Highcharts.Dictionary<number>} [kinds]
  26665. * @return {Highcharts.PointGraphicalProps}
  26666. */
  26667. getGraphicalProps: function (kinds) {
  26668. var point = this, props = [], prop, i, graphicalProps = { singular: [], plural: [] };
  26669. kinds = kinds || { graphic: 1, dataLabel: 1 };
  26670. if (kinds.graphic) {
  26671. props.push('graphic', 'shadowGroup');
  26672. }
  26673. if (kinds.dataLabel) {
  26674. props.push('dataLabel', 'dataLabelUpper', 'connector');
  26675. }
  26676. i = props.length;
  26677. while (i--) {
  26678. prop = props[i];
  26679. if (point[prop]) {
  26680. graphicalProps.singular.push(prop);
  26681. }
  26682. }
  26683. ['dataLabel', 'connector'].forEach(function (prop) {
  26684. var plural = prop + 's';
  26685. if (kinds[prop] && point[plural]) {
  26686. graphicalProps.plural.push(plural);
  26687. }
  26688. });
  26689. return graphicalProps;
  26690. },
  26691. /**
  26692. * Return the configuration hash needed for the data label and tooltip
  26693. * formatters.
  26694. *
  26695. * @function Highcharts.Point#getLabelConfig
  26696. *
  26697. * @return {Highcharts.PointLabelObject}
  26698. * Abstract object used in formatters and formats.
  26699. */
  26700. getLabelConfig: function () {
  26701. return {
  26702. x: this.category,
  26703. y: this.y,
  26704. color: this.color,
  26705. colorIndex: this.colorIndex,
  26706. key: this.name || this.category,
  26707. series: this.series,
  26708. point: this,
  26709. percentage: this.percentage,
  26710. total: this.total || this.stackTotal
  26711. };
  26712. },
  26713. /**
  26714. * Extendable method for formatting each point's tooltip line.
  26715. *
  26716. * @function Highcharts.Point#tooltipFormatter
  26717. *
  26718. * @param {string} pointFormat
  26719. * The point format.
  26720. *
  26721. * @return {string}
  26722. * A string to be concatenated in to the common tooltip text.
  26723. */
  26724. tooltipFormatter: function (pointFormat) {
  26725. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  26726. var series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  26727. // Replace default point style with class name
  26728. if (series.chart.styledMode) {
  26729. pointFormat =
  26730. series.chart.tooltip.styledModeFormat(pointFormat);
  26731. }
  26732. // Loop over the point array map and replace unformatted values with
  26733. // sprintf formatting markup
  26734. (series.pointArrayMap || ['y']).forEach(function (key) {
  26735. key = '{point.' + key; // without the closing bracket
  26736. if (valuePrefix || valueSuffix) {
  26737. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  26738. }
  26739. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  26740. });
  26741. return format(pointFormat, {
  26742. point: this,
  26743. series: this.series
  26744. }, series.chart);
  26745. },
  26746. /**
  26747. * Fire an event on the Point object.
  26748. *
  26749. * @private
  26750. * @function Highcharts.Point#firePointEvent
  26751. *
  26752. * @param {string} eventType
  26753. * Type of the event.
  26754. *
  26755. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  26756. * Additional event arguments.
  26757. *
  26758. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  26759. * Default event handler.
  26760. *
  26761. * @fires Highcharts.Point#event:*
  26762. */
  26763. firePointEvent: function (eventType, eventArgs, defaultFunction) {
  26764. var point = this, series = this.series, seriesOptions = series.options;
  26765. // load event handlers on demand to save time on mouseover/out
  26766. if (seriesOptions.point.events[eventType] ||
  26767. (point.options &&
  26768. point.options.events &&
  26769. point.options.events[eventType])) {
  26770. this.importEvents();
  26771. }
  26772. // add default handler if in selection mode
  26773. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  26774. defaultFunction = function (event) {
  26775. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  26776. // for Opera.
  26777. if (point.select) { // #2911
  26778. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  26779. }
  26780. };
  26781. }
  26782. fireEvent(this, eventType, eventArgs, defaultFunction);
  26783. },
  26784. /**
  26785. * For categorized axes this property holds the category name for the
  26786. * point. For other axes it holds the X value.
  26787. *
  26788. * @name Highcharts.Point#category
  26789. * @type {number|string}
  26790. */
  26791. /**
  26792. * The name of the point. The name can be given as the first position of the
  26793. * point configuration array, or as a `name` property in the configuration:
  26794. *
  26795. * @example
  26796. * // Array config
  26797. * data: [
  26798. * ['John', 1],
  26799. * ['Jane', 2]
  26800. * ]
  26801. *
  26802. * // Object config
  26803. * data: [{
  26804. * name: 'John',
  26805. * y: 1
  26806. * }, {
  26807. * name: 'Jane',
  26808. * y: 2
  26809. * }]
  26810. *
  26811. * @name Highcharts.Point#name
  26812. * @type {string}
  26813. */
  26814. /**
  26815. * The percentage for points in a stacked series or pies.
  26816. *
  26817. * @name Highcharts.Point#percentage
  26818. * @type {number}
  26819. */
  26820. /**
  26821. * The total of values in either a stack for stacked series, or a pie in a
  26822. * pie series.
  26823. *
  26824. * @name Highcharts.Point#total
  26825. * @type {number}
  26826. */
  26827. /**
  26828. * For certain series types, like pie charts, where individual points can
  26829. * be shown or hidden.
  26830. *
  26831. * @name Highcharts.Point#visible
  26832. * @type {boolean}
  26833. */
  26834. visible: true
  26835. };
  26836. });
  26837. _registerModule(_modules, 'parts/Series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  26838. /* *
  26839. *
  26840. * (c) 2010-2019 Torstein Honsi
  26841. *
  26842. * License: www.highcharts.com/license
  26843. *
  26844. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  26845. *
  26846. * */
  26847. /**
  26848. * This is a placeholder type of the possible series options for
  26849. * [Highcharts](../highcharts/series), [Highstock](../highstock/series),
  26850. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  26851. *
  26852. * In TypeScript is this dynamically generated to reference all possible types
  26853. * of series options.
  26854. *
  26855. * @ignore-declaration
  26856. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  26857. */
  26858. /**
  26859. * Options for `dataSorting`.
  26860. *
  26861. * @interface Highcharts.DataSortingOptionsObject
  26862. * @since 8.0.0
  26863. */ /**
  26864. * Enable or disable data sorting for the series.
  26865. * @name Highcharts.DataSortingOptionsObject#enabled
  26866. * @type {boolean|undefined}
  26867. */ /**
  26868. * Whether to allow matching points by name in an update.
  26869. * @name Highcharts.DataSortingOptionsObject#matchByName
  26870. * @type {boolean|undefined}
  26871. */ /**
  26872. * Determines what data value should be used to sort by.
  26873. * @name Highcharts.DataSortingOptionsObject#sortKey
  26874. * @type {string|undefined}
  26875. */
  26876. /**
  26877. * Function callback when a series has been animated.
  26878. *
  26879. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  26880. *
  26881. * @param {Highcharts.Series} this
  26882. * The series where the event occured.
  26883. *
  26884. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  26885. * Event arguments.
  26886. */
  26887. /**
  26888. * Event information regarding completed animation of a series.
  26889. *
  26890. * @interface Highcharts.SeriesAfterAnimateEventObject
  26891. */ /**
  26892. * Animated series.
  26893. * @name Highcharts.SeriesAfterAnimateEventObject#target
  26894. * @type {Highcharts.Series}
  26895. */ /**
  26896. * Event type.
  26897. * @name Highcharts.SeriesAfterAnimateEventObject#type
  26898. * @type {"afterAnimate"}
  26899. */
  26900. /**
  26901. * Function callback when the checkbox next to the series' name in the legend is
  26902. * clicked.
  26903. *
  26904. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  26905. *
  26906. * @param {Highcharts.Series} this
  26907. * The series where the event occured.
  26908. *
  26909. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  26910. * Event arguments.
  26911. */
  26912. /**
  26913. * Event information regarding check of a series box.
  26914. *
  26915. * @interface Highcharts.SeriesCheckboxClickEventObject
  26916. */ /**
  26917. * Whether the box has been checked.
  26918. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  26919. * @type {boolean}
  26920. */ /**
  26921. * Related series.
  26922. * @name Highcharts.SeriesCheckboxClickEventObject#item
  26923. * @type {Highcharts.Series}
  26924. */ /**
  26925. * Related series.
  26926. * @name Highcharts.SeriesCheckboxClickEventObject#target
  26927. * @type {Highcharts.Series}
  26928. */ /**
  26929. * Event type.
  26930. * @name Highcharts.SeriesCheckboxClickEventObject#type
  26931. * @type {"checkboxClick"}
  26932. */
  26933. /**
  26934. * Function callback when a series is clicked. Return false to cancel toogle
  26935. * actions.
  26936. *
  26937. * @callback Highcharts.SeriesClickCallbackFunction
  26938. *
  26939. * @param {Highcharts.Series} this
  26940. * The series where the event occured.
  26941. *
  26942. * @param {Highcharts.SeriesClickEventObject} event
  26943. * Event arguments.
  26944. */
  26945. /**
  26946. * Common information for a click event on a series.
  26947. *
  26948. * @interface Highcharts.SeriesClickEventObject
  26949. * @extends global.Event
  26950. */ /**
  26951. * Nearest point on the graph.
  26952. * @name Highcharts.SeriesClickEventObject#point
  26953. * @type {Highcharts.Point}
  26954. */
  26955. /**
  26956. * Gets fired when the series is hidden after chart generation time, either by
  26957. * clicking the legend item or by calling `.hide()`.
  26958. *
  26959. * @callback Highcharts.SeriesHideCallbackFunction
  26960. *
  26961. * @param {Highcharts.Series} this
  26962. * The series where the event occured.
  26963. *
  26964. * @param {global.Event} event
  26965. * The event that occured.
  26966. */
  26967. /**
  26968. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  26969. * graph.
  26970. *
  26971. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  26972. */
  26973. /**
  26974. * Gets fired when the legend item belonging to the series is clicked. The
  26975. * default action is to toggle the visibility of the series. This can be
  26976. * prevented by returning `false` or calling `event.preventDefault()`.
  26977. *
  26978. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  26979. *
  26980. * @param {Highcharts.Series} this
  26981. * The series where the event occured.
  26982. *
  26983. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  26984. * The event that occured.
  26985. */
  26986. /**
  26987. * Information about the event.
  26988. *
  26989. * @interface Highcharts.SeriesLegendItemClickEventObject
  26990. */ /**
  26991. * Related browser event.
  26992. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  26993. * @type {global.PointerEvent}
  26994. */ /**
  26995. * Prevent the default action of toggle the visibility of the series.
  26996. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  26997. * @type {Function}
  26998. */ /**
  26999. * Related series.
  27000. * @name Highcharts.SeriesCheckboxClickEventObject#target
  27001. * @type {Highcharts.Series}
  27002. */ /**
  27003. * Event type.
  27004. * @name Highcharts.SeriesCheckboxClickEventObject#type
  27005. * @type {"checkboxClick"}
  27006. */
  27007. /**
  27008. * Gets fired when the mouse leaves the graph.
  27009. *
  27010. * @callback Highcharts.SeriesMouseOutCallbackFunction
  27011. *
  27012. * @param {Highcharts.Series} this
  27013. * Series where the event occured.
  27014. *
  27015. * @param {global.PointerEvent} event
  27016. * Event that occured.
  27017. */
  27018. /**
  27019. * Gets fired when the mouse enters the graph.
  27020. *
  27021. * @callback Highcharts.SeriesMouseOverCallbackFunction
  27022. *
  27023. * @param {Highcharts.Series} this
  27024. * Series where the event occured.
  27025. *
  27026. * @param {global.PointerEvent} event
  27027. * Event that occured.
  27028. */
  27029. /**
  27030. * Translation and scale for the plot area of a series.
  27031. *
  27032. * @interface Highcharts.SeriesPlotBoxObject
  27033. */ /**
  27034. * @name Highcharts.SeriesPlotBoxObject#scaleX
  27035. * @type {number}
  27036. */ /**
  27037. * @name Highcharts.SeriesPlotBoxObject#scaleY
  27038. * @type {number}
  27039. */ /**
  27040. * @name Highcharts.SeriesPlotBoxObject#translateX
  27041. * @type {number}
  27042. */ /**
  27043. * @name Highcharts.SeriesPlotBoxObject#translateY
  27044. * @type {number}
  27045. */
  27046. /**
  27047. * Gets fired when the series is shown after chart generation time, either by
  27048. * clicking the legend item or by calling `.show()`.
  27049. *
  27050. * @callback Highcharts.SeriesShowCallbackFunction
  27051. *
  27052. * @param {Highcharts.Series} this
  27053. * Series where the event occured.
  27054. *
  27055. * @param {global.Event} event
  27056. * Event that occured.
  27057. */
  27058. /**
  27059. * Possible key values for the series state options.
  27060. *
  27061. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  27062. */
  27063. var animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isString = U.isString, objectEach = U.objectEach, pick = U.pick, splat = U.splat, syncTimeout = U.syncTimeout;
  27064. var addEvent = H.addEvent, defaultOptions = H.defaultOptions, defaultPlotOptions = H.defaultPlotOptions, fireEvent = H.fireEvent, LegendSymbolMixin = H.LegendSymbolMixin, // @todo add as a requirement
  27065. merge = H.merge, Point = H.Point, // @todo add as a requirement
  27066. removeEvent = H.removeEvent, SVGElement = H.SVGElement, win = H.win;
  27067. /**
  27068. * This is the base series prototype that all other series types inherit from.
  27069. * A new series is initialized either through the
  27070. * [series](https://api.highcharts.com/highcharts/series)
  27071. * option structure, or after the chart is initialized, through
  27072. * {@link Highcharts.Chart#addSeries}.
  27073. *
  27074. * The object can be accessed in a number of ways. All series and point event
  27075. * handlers give a reference to the `series` object. The chart object has a
  27076. * {@link Highcharts.Chart#series|series} property that is a collection of all
  27077. * the chart's series. The point objects and axis objects also have the same
  27078. * reference.
  27079. *
  27080. * Another way to reference the series programmatically is by `id`. Add an id
  27081. * in the series configuration options, and get the series object by
  27082. * {@link Highcharts.Chart#get}.
  27083. *
  27084. * Configuration options for the series are given in three levels. Options for
  27085. * all series in a chart are given in the
  27086. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  27087. * object. Then options for all series of a specific type
  27088. * are given in the plotOptions of that type, for example `plotOptions.line`.
  27089. * Next, options for one single series are given in the series array, or as
  27090. * arguments to `chart.addSeries`.
  27091. *
  27092. * The data in the series is stored in various arrays.
  27093. *
  27094. * - First, `series.options.data` contains all the original config options for
  27095. * each point whether added by options or methods like `series.addPoint`.
  27096. *
  27097. * - Next, `series.data` contains those values converted to points, but in case
  27098. * the series data length exceeds the `cropThreshold`, or if the data is
  27099. * grouped, `series.data` doesn't contain all the points. It only contains the
  27100. * points that have been created on demand.
  27101. *
  27102. * - Then there's `series.points` that contains all currently visible point
  27103. * objects. In case of cropping, the cropped-away points are not part of this
  27104. * array. The `series.points` array starts at `series.cropStart` compared to
  27105. * `series.data` and `series.options.data`. If however the series data is
  27106. * grouped, these can't be correlated one to one.
  27107. *
  27108. * - `series.xData` and `series.processedXData` contain clean x values,
  27109. * equivalent to `series.data` and `series.points`.
  27110. *
  27111. * - `series.yData` and `series.processedYData` contain clean y values,
  27112. * equivalent to `series.data` and `series.points`.
  27113. *
  27114. * @class
  27115. * @name Highcharts.Series
  27116. *
  27117. * @param {Highcharts.Chart} chart
  27118. * The chart instance.
  27119. *
  27120. * @param {Highcharts.SeriesOptionsType|object} options
  27121. * The series options.
  27122. */ /**
  27123. * The line series is the base type and is therefor the series base prototype.
  27124. *
  27125. * @private
  27126. * @class
  27127. * @name Highcharts.seriesTypes.line
  27128. *
  27129. * @augments Highcharts.Series
  27130. */
  27131. H.Series = H.seriesType('line',
  27132. /**
  27133. * Series options for specific data and the data itself. In TypeScript you
  27134. * have to cast the series options to specific series types, to get all
  27135. * possible options for a series.
  27136. *
  27137. * @example
  27138. * // TypeScript example
  27139. * Highcharts.chart('container', {
  27140. * series: [{
  27141. * color: '#06C',
  27142. * data: [[0, 1], [2, 3]]
  27143. * } as Highcharts.SeriesLineOptions ]
  27144. * });
  27145. *
  27146. * @type {Array<*>}
  27147. * @apioption series
  27148. */
  27149. /**
  27150. * An id for the series. This can be used after render time to get a pointer
  27151. * to the series object through `chart.get()`.
  27152. *
  27153. * @sample {highcharts} highcharts/plotoptions/series-id/
  27154. * Get series by id
  27155. *
  27156. * @type {string}
  27157. * @since 1.2.0
  27158. * @apioption series.id
  27159. */
  27160. /**
  27161. * The index of the series in the chart, affecting the internal index in the
  27162. * `chart.series` array, the visible Z index as well as the order in the
  27163. * legend.
  27164. *
  27165. * @type {number}
  27166. * @since 2.3.0
  27167. * @apioption series.index
  27168. */
  27169. /**
  27170. * The sequential index of the series in the legend.
  27171. *
  27172. * @see [legend.reversed](#legend.reversed),
  27173. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  27174. *
  27175. * @sample {highcharts|highstock} highcharts/series/legendindex/
  27176. * Legend in opposite order
  27177. *
  27178. * @type {number}
  27179. * @apioption series.legendIndex
  27180. */
  27181. /**
  27182. * The name of the series as shown in the legend, tooltip etc.
  27183. *
  27184. * @sample {highcharts} highcharts/series/name/
  27185. * Series name
  27186. * @sample {highmaps} maps/demo/category-map/
  27187. * Series name
  27188. *
  27189. * @type {string}
  27190. * @apioption series.name
  27191. */
  27192. /**
  27193. * This option allows grouping series in a stacked chart. The stack option
  27194. * can be a string or anything else, as long as the grouped series' stack
  27195. * options match each other after conversion into a string.
  27196. *
  27197. * @sample {highcharts} highcharts/series/stack/
  27198. * Stacked and grouped columns
  27199. *
  27200. * @type {number|string}
  27201. * @since 2.1
  27202. * @product highcharts highstock
  27203. * @apioption series.stack
  27204. */
  27205. /**
  27206. * The type of series, for example `line` or `column`. By default, the
  27207. * series type is inherited from [chart.type](#chart.type), so unless the
  27208. * chart is a combination of series types, there is no need to set it on the
  27209. * series level.
  27210. *
  27211. * @sample {highcharts} highcharts/series/type/
  27212. * Line and column in the same chart
  27213. * @sample highcharts/series/type-dynamic/
  27214. * Dynamic types with button selector
  27215. * @sample {highmaps} maps/demo/mapline-mappoint/
  27216. * Multiple types in the same map
  27217. *
  27218. * @type {string}
  27219. * @apioption series.type
  27220. */
  27221. /**
  27222. * When using dual or multiple x axes, this number defines which xAxis the
  27223. * particular series is connected to. It refers to either the
  27224. * {@link #xAxis.id|axis id}
  27225. * or the index of the axis in the xAxis array, with 0 being the first.
  27226. *
  27227. * @type {number|string}
  27228. * @default 0
  27229. * @product highcharts highstock
  27230. * @apioption series.xAxis
  27231. */
  27232. /**
  27233. * When using dual or multiple y axes, this number defines which yAxis the
  27234. * particular series is connected to. It refers to either the
  27235. * {@link #yAxis.id|axis id}
  27236. * or the index of the axis in the yAxis array, with 0 being the first.
  27237. *
  27238. * @sample {highcharts} highcharts/series/yaxis/
  27239. * Apply the column series to the secondary Y axis
  27240. *
  27241. * @type {number|string}
  27242. * @default 0
  27243. * @product highcharts highstock
  27244. * @apioption series.yAxis
  27245. */
  27246. /**
  27247. * Define the visual z index of the series.
  27248. *
  27249. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  27250. * With no z index, the series defined last are on top
  27251. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  27252. * With a z index, the series with the highest z index is on top
  27253. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  27254. * With no z index, the series defined last are on top
  27255. * @sample {highstock} highcharts/plotoptions/series-zindex/
  27256. * With a z index, the series with the highest z index is on top
  27257. *
  27258. * @type {number}
  27259. * @product highcharts highstock
  27260. * @apioption series.zIndex
  27261. */
  27262. null,
  27263. /**
  27264. * General options for all series types.
  27265. *
  27266. * @optionparent plotOptions.series
  27267. */
  27268. {
  27269. /**
  27270. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  27271. * of a line graph. Round means that lines are rounded in the ends and
  27272. * bends.
  27273. *
  27274. * @type {Highcharts.SeriesLinecapValue}
  27275. * @default round
  27276. * @since 3.0.7
  27277. * @apioption plotOptions.line.linecap
  27278. */
  27279. /**
  27280. * Pixel width of the graph line.
  27281. *
  27282. * @see In styled mode, the line stroke-width can be set with the
  27283. * `.highcharts-graph` class name.
  27284. *
  27285. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  27286. * On all series
  27287. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  27288. * On one single series
  27289. *
  27290. * @product highcharts highstock
  27291. *
  27292. * @private
  27293. */
  27294. lineWidth: 2,
  27295. /**
  27296. * For some series, there is a limit that shuts down initial animation
  27297. * by default when the total number of points in the chart is too high.
  27298. * For example, for a column chart and its derivatives, animation does
  27299. * not run if there is more than 250 points totally. To disable this
  27300. * cap, set `animationLimit` to `Infinity`.
  27301. *
  27302. * @type {number}
  27303. * @apioption plotOptions.series.animationLimit
  27304. */
  27305. /**
  27306. * Allow this series' points to be selected by clicking on the graphic
  27307. * (columns, point markers, pie slices, map areas etc).
  27308. *
  27309. * The selected points can be handled by point select and unselect
  27310. * events, or collectively by the [getSelectedPoints](
  27311. * Highcharts.Chart#getSelectedPoints) function.
  27312. *
  27313. * And alternative way of selecting points is through dragging.
  27314. *
  27315. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  27316. * Line
  27317. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  27318. * Column
  27319. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  27320. * Pie
  27321. * @sample {highcharts} highcharts/chart/events-selection-points/
  27322. * Select a range of points through a drag selection
  27323. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  27324. * Map area
  27325. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  27326. * Map bubble
  27327. *
  27328. * @since 1.2.0
  27329. *
  27330. * @private
  27331. */
  27332. allowPointSelect: false,
  27333. /**
  27334. * If true, a checkbox is displayed next to the legend item to allow
  27335. * selecting the series. The state of the checkbox is determined by
  27336. * the `selected` option.
  27337. *
  27338. * @productdesc {highmaps}
  27339. * Note that if a `colorAxis` is defined, the color axis is represented
  27340. * in the legend, not the series.
  27341. *
  27342. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  27343. * Show select box
  27344. *
  27345. * @since 1.2.0
  27346. *
  27347. * @private
  27348. */
  27349. showCheckbox: false,
  27350. /**
  27351. * Enable or disable the initial animation when a series is displayed.
  27352. * The animation can also be set as a configuration object. Please
  27353. * note that this option only applies to the initial animation of the
  27354. * series itself. For other animations, see [chart.animation](
  27355. * #chart.animation) and the animation parameter under the API methods.
  27356. * The following properties are supported:
  27357. *
  27358. * - `duration`: The duration of the animation in milliseconds.
  27359. *
  27360. * - `easing`: Can be a string reference to an easing function set on
  27361. * the `Math` object or a function. See the _Custom easing function_
  27362. * demo below.
  27363. *
  27364. * Due to poor performance, animation is disabled in old IE browsers
  27365. * for several chart types.
  27366. *
  27367. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  27368. * Animation disabled
  27369. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  27370. * Slower animation
  27371. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  27372. * Custom easing function
  27373. * @sample {highstock} stock/plotoptions/animation-slower/
  27374. * Slower animation
  27375. * @sample {highstock} stock/plotoptions/animation-easing/
  27376. * Custom easing function
  27377. * @sample {highmaps} maps/plotoptions/series-animation-true/
  27378. * Animation enabled on map series
  27379. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  27380. * Disabled on mapbubble series
  27381. *
  27382. * @type {boolean|Highcharts.AnimationOptionsObject}
  27383. * @default {highcharts} true
  27384. * @default {highstock} true
  27385. * @default {highmaps} false
  27386. *
  27387. * @private
  27388. */
  27389. animation: {
  27390. /** @internal */
  27391. duration: 1000
  27392. },
  27393. /**
  27394. * An additional class name to apply to the series' graphical elements.
  27395. * This option does not replace default class names of the graphical
  27396. * element.
  27397. *
  27398. * @type {string}
  27399. * @since 5.0.0
  27400. * @apioption plotOptions.series.className
  27401. */
  27402. /**
  27403. * Disable this option to allow series rendering in the whole plotting
  27404. * area.
  27405. *
  27406. * **Note:** Clipping should be always enabled when
  27407. * [chart.zoomType](#chart.zoomType) is set
  27408. *
  27409. * @sample {highcharts} highcharts/plotoptions/series-clip/
  27410. * Disabled clipping
  27411. *
  27412. * @default true
  27413. * @type {boolean}
  27414. * @since 3.0.0
  27415. * @apioption plotOptions.series.clip
  27416. */
  27417. /**
  27418. * The main color of the series. In line type series it applies to the
  27419. * line and the point markers unless otherwise specified. In bar type
  27420. * series it applies to the bars unless a color is specified per point.
  27421. * The default value is pulled from the `options.colors` array.
  27422. *
  27423. * In styled mode, the color can be defined by the
  27424. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  27425. * color can be set with the `.highcharts-series`,
  27426. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  27427. * `.highcharts-series-{n}` class, or individual classes given by the
  27428. * `className` option.
  27429. *
  27430. * @productdesc {highmaps}
  27431. * In maps, the series color is rarely used, as most choropleth maps use
  27432. * the color to denote the value of each point. The series color can
  27433. * however be used in a map with multiple series holding categorized
  27434. * data.
  27435. *
  27436. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  27437. * General plot option
  27438. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  27439. * One specific series
  27440. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  27441. * Area color
  27442. * @sample {highcharts} highcharts/series/infographic/
  27443. * Pattern fill
  27444. * @sample {highmaps} maps/demo/category-map/
  27445. * Category map by multiple series
  27446. *
  27447. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  27448. * @apioption plotOptions.series.color
  27449. */
  27450. /**
  27451. * Styled mode only. A specific color index to use for the series, so
  27452. * its graphic representations are given the class name
  27453. * `highcharts-color-{n}`.
  27454. *
  27455. * @type {number}
  27456. * @since 5.0.0
  27457. * @apioption plotOptions.series.colorIndex
  27458. */
  27459. /**
  27460. * Whether to connect a graph line across null points, or render a gap
  27461. * between the two points on either side of the null.
  27462. *
  27463. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  27464. * False by default
  27465. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  27466. * True
  27467. *
  27468. * @type {boolean}
  27469. * @default false
  27470. * @product highcharts highstock
  27471. * @apioption plotOptions.series.connectNulls
  27472. */
  27473. /**
  27474. * You can set the cursor to "pointer" if you have click events attached
  27475. * to the series, to signal to the user that the points and lines can
  27476. * be clicked.
  27477. *
  27478. * In styled mode, the series cursor can be set with the same classes
  27479. * as listed under [series.color](#plotOptions.series.color).
  27480. *
  27481. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  27482. * On line graph
  27483. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  27484. * On columns
  27485. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  27486. * On scatter markers
  27487. * @sample {highstock} stock/plotoptions/cursor/
  27488. * Pointer on a line graph
  27489. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  27490. * Map area
  27491. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  27492. * Map bubble
  27493. *
  27494. * @type {string|Highcharts.CursorValue}
  27495. * @apioption plotOptions.series.cursor
  27496. */
  27497. /**
  27498. * A name for the dash style to use for the graph, or for some series
  27499. * types the outline of each shape.
  27500. *
  27501. * In styled mode, the
  27502. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  27503. * can be set with the same classes as listed under
  27504. * [series.color](#plotOptions.series.color).
  27505. *
  27506. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  27507. * Possible values demonstrated
  27508. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  27509. * Chart suitable for printing in black and white
  27510. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  27511. * Possible values demonstrated
  27512. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  27513. * Possible values demonstrated
  27514. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  27515. * Dotted borders on a map
  27516. *
  27517. * @type {Highcharts.DashStyleValue}
  27518. * @default Solid
  27519. * @since 2.1
  27520. * @apioption plotOptions.series.dashStyle
  27521. */
  27522. /**
  27523. * A description of the series to add to the screen reader information
  27524. * about the series.
  27525. *
  27526. * @type {string}
  27527. * @since 5.0.0
  27528. * @requires modules/accessibility
  27529. * @apioption plotOptions.series.description
  27530. */
  27531. /**
  27532. * Options for the series data sorting.
  27533. *
  27534. * @type {Highcharts.DataSortingOptionsObject}
  27535. * @since 8.0.0
  27536. * @product highcharts highstock
  27537. * @apioption plotOptions.series.dataSorting
  27538. */
  27539. /**
  27540. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  27541. * #xAxis.reversed) to change the sorting order.
  27542. *
  27543. * @sample {highcharts} highcharts/datasorting/animation/
  27544. * Data sorting in scatter-3d
  27545. * @sample {highcharts} highcharts/datasorting/labels-animation/
  27546. * Axis labels animation
  27547. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  27548. * Dependent series sorting
  27549. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  27550. * Independent series sorting
  27551. *
  27552. * @type {boolean}
  27553. * @since 8.0.0
  27554. * @apioption plotOptions.series.dataSorting.enabled
  27555. */
  27556. /**
  27557. * Whether to allow matching points by name in an update. If this option
  27558. * is disabled, points will be matched by order.
  27559. *
  27560. * @sample {highcharts} highcharts/datasorting/match-by-name/
  27561. * Enabled match by name
  27562. *
  27563. * @type {boolean}
  27564. * @since 8.0.0
  27565. * @apioption plotOptions.series.dataSorting.matchByName
  27566. */
  27567. /**
  27568. * Determines what data value should be used to sort by.
  27569. *
  27570. * @sample {highcharts} highcharts/datasorting/sort-key/
  27571. * Sort key as `z` value
  27572. *
  27573. * @type {string}
  27574. * @since 8.0.0
  27575. * @default y
  27576. * @apioption plotOptions.series.dataSorting.sortKey
  27577. */
  27578. /**
  27579. * Enable or disable the mouse tracking for a specific series. This
  27580. * includes point tooltips and click events on graphs and points. For
  27581. * large datasets it improves performance.
  27582. *
  27583. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  27584. * No mouse tracking
  27585. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  27586. * No mouse tracking
  27587. *
  27588. * @type {boolean}
  27589. * @default true
  27590. * @apioption plotOptions.series.enableMouseTracking
  27591. */
  27592. /**
  27593. * Whether to use the Y extremes of the total chart width or only the
  27594. * zoomed area when zooming in on parts of the X axis. By default, the
  27595. * Y axis adjusts to the min and max of the visible data. Cartesian
  27596. * series only.
  27597. *
  27598. * @type {boolean}
  27599. * @default false
  27600. * @since 4.1.6
  27601. * @product highcharts highstock gantt
  27602. * @apioption plotOptions.series.getExtremesFromAll
  27603. */
  27604. /**
  27605. * An array specifying which option maps to which key in the data point
  27606. * array. This makes it convenient to work with unstructured data arrays
  27607. * from different sources.
  27608. *
  27609. * @see [series.data](#series.line.data)
  27610. *
  27611. * @sample {highcharts|highstock} highcharts/series/data-keys/
  27612. * An extended data array with keys
  27613. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  27614. * Nested keys used to access object properties
  27615. *
  27616. * @type {Array<string>}
  27617. * @since 4.1.6
  27618. * @apioption plotOptions.series.keys
  27619. */
  27620. /**
  27621. * The line cap used for line ends and line joins on the graph.
  27622. *
  27623. * @type {Highcharts.SeriesLinecapValue}
  27624. * @default round
  27625. * @product highcharts highstock
  27626. * @apioption plotOptions.series.linecap
  27627. */
  27628. /**
  27629. * The [id](#series.id) of another series to link to. Additionally,
  27630. * the value can be ":previous" to link to the previous series. When
  27631. * two series are linked, only the first one appears in the legend.
  27632. * Toggling the visibility of this also toggles the linked series.
  27633. *
  27634. * If master series uses data sorting and linked series does not have
  27635. * its own sorting definition, the linked series will be sorted in the
  27636. * same order as the master one.
  27637. *
  27638. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  27639. * Linked series
  27640. *
  27641. * @type {string}
  27642. * @since 3.0
  27643. * @product highcharts highstock gantt
  27644. * @apioption plotOptions.series.linkedTo
  27645. */
  27646. /**
  27647. * Options for the corresponding navigator series if `showInNavigator`
  27648. * is `true` for this series. Available options are the same as any
  27649. * series, documented at [plotOptions](#plotOptions.series) and
  27650. * [series](#series).
  27651. *
  27652. * These options are merged with options in [navigator.series](
  27653. * #navigator.series), and will take precedence if the same option is
  27654. * defined both places.
  27655. *
  27656. * @see [navigator.series](#navigator.series)
  27657. *
  27658. * @type {Highcharts.PlotSeriesOptions}
  27659. * @since 5.0.0
  27660. * @product highstock
  27661. * @apioption plotOptions.series.navigatorOptions
  27662. */
  27663. /**
  27664. * The color for the parts of the graph or points that are below the
  27665. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  27666. * precedence over the negative color. Using `negativeColor` is
  27667. * equivalent to applying a zone with value of 0.
  27668. *
  27669. * @see In styled mode, a negative color is applied by setting this option
  27670. * to `true` combined with the `.highcharts-negative` class name.
  27671. *
  27672. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  27673. * Spline, area and column
  27674. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  27675. * Arearange
  27676. * @sample {highcharts} highcharts/css/series-negative-color/
  27677. * Styled mode
  27678. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  27679. * Spline, area and column
  27680. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  27681. * Arearange
  27682. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  27683. * Spline, area and column
  27684. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  27685. * Arearange
  27686. *
  27687. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  27688. * @since 3.0
  27689. * @apioption plotOptions.series.negativeColor
  27690. */
  27691. /**
  27692. * Same as
  27693. * [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
  27694. * but for an individual series. Overrides the chart wide configuration.
  27695. *
  27696. * @type {Function}
  27697. * @since 5.0.12
  27698. * @apioption plotOptions.series.pointDescriptionFormatter
  27699. */
  27700. /**
  27701. * If no x values are given for the points in a series, `pointInterval`
  27702. * defines the interval of the x values. For example, if a series
  27703. * contains one value every decade starting from year 0, set
  27704. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  27705. * is set in milliseconds.
  27706. *
  27707. * It can be also be combined with `pointIntervalUnit` to draw irregular
  27708. * time intervals.
  27709. *
  27710. * Please note that this options applies to the _series data_, not the
  27711. * interval of the axis ticks, which is independent.
  27712. *
  27713. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  27714. * Datetime X axis
  27715. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  27716. * Using pointStart and pointInterval
  27717. *
  27718. * @type {number}
  27719. * @default 1
  27720. * @product highcharts highstock gantt
  27721. * @apioption plotOptions.series.pointInterval
  27722. */
  27723. /**
  27724. * On datetime series, this allows for setting the
  27725. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  27726. * units, `day`, `month` and `year`. A day is usually the same as 24
  27727. * hours, but `pointIntervalUnit` also takes the DST crossover into
  27728. * consideration when dealing with local time. Combine this option with
  27729. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  27730. *
  27731. * Please note that this options applies to the _series data_, not the
  27732. * interval of the axis ticks, which is independent.
  27733. *
  27734. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  27735. * One point a month
  27736. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  27737. * One point a month
  27738. *
  27739. * @type {string}
  27740. * @since 4.1.0
  27741. * @product highcharts highstock gantt
  27742. * @validvalue ["day", "month", "year"]
  27743. * @apioption plotOptions.series.pointIntervalUnit
  27744. */
  27745. /**
  27746. * Possible values: `"on"`, `"between"`, `number`.
  27747. *
  27748. * In a column chart, when pointPlacement is `"on"`, the point will not
  27749. * create any padding of the X axis. In a polar column chart this means
  27750. * that the first column points directly north. If the pointPlacement is
  27751. * `"between"`, the columns will be laid out between ticks. This is
  27752. * useful for example for visualising an amount between two points in
  27753. * time or in a certain sector of a polar chart.
  27754. *
  27755. * Since Highcharts 3.0.2, the point placement can also be numeric,
  27756. * where 0 is on the axis value, -0.5 is between this value and the
  27757. * previous, and 0.5 is between this value and the next. Unlike the
  27758. * textual options, numeric point placement options won't affect axis
  27759. * padding.
  27760. *
  27761. * Note that pointPlacement needs a [pointRange](
  27762. * #plotOptions.series.pointRange) to work. For column series this is
  27763. * computed, but for line-type series it needs to be set.
  27764. *
  27765. * For the `xrange` series type and gantt charts, if the Y axis is a
  27766. * category axis, the `pointPlacement` applies to the Y axis rather than
  27767. * the (typically datetime) X axis.
  27768. *
  27769. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  27770. * charts.
  27771. *
  27772. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  27773. *
  27774. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  27775. * Between in a column chart
  27776. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  27777. * Numeric placement for custom layout
  27778. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  27779. * Placement in heatmap
  27780. *
  27781. * @type {string|number}
  27782. * @since 2.3.0
  27783. * @product highcharts highstock gantt
  27784. * @apioption plotOptions.series.pointPlacement
  27785. */
  27786. /**
  27787. * If no x values are given for the points in a series, pointStart
  27788. * defines on what value to start. For example, if a series contains one
  27789. * yearly value starting from 1945, set pointStart to 1945.
  27790. *
  27791. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  27792. * Linear
  27793. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  27794. * Datetime
  27795. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  27796. * Using pointStart and pointInterval
  27797. *
  27798. * @type {number}
  27799. * @default 0
  27800. * @product highcharts highstock gantt
  27801. * @apioption plotOptions.series.pointStart
  27802. */
  27803. /**
  27804. * Whether to select the series initially. If `showCheckbox` is true,
  27805. * the checkbox next to the series name in the legend will be checked
  27806. * for a selected series.
  27807. *
  27808. * @sample {highcharts} highcharts/plotoptions/series-selected/
  27809. * One out of two series selected
  27810. *
  27811. * @type {boolean}
  27812. * @default false
  27813. * @since 1.2.0
  27814. * @apioption plotOptions.series.selected
  27815. */
  27816. /**
  27817. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  27818. * shadow can be an object configuration containing `color`, `offsetX`,
  27819. * `offsetY`, `opacity` and `width`.
  27820. *
  27821. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  27822. * Shadow enabled
  27823. *
  27824. * @type {boolean|Highcharts.ShadowOptionsObject}
  27825. * @default false
  27826. * @apioption plotOptions.series.shadow
  27827. */
  27828. /**
  27829. * Whether to display this particular series or series type in the
  27830. * legend. Standalone series are shown in legend by default, and linked
  27831. * series are not. Since v7.2.0 it is possible to show series that use
  27832. * colorAxis by setting this option to `true`.
  27833. *
  27834. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  27835. * One series in the legend, one hidden
  27836. *
  27837. * @type {boolean}
  27838. * @apioption plotOptions.series.showInLegend
  27839. */
  27840. /**
  27841. * Whether or not to show the series in the navigator. Takes precedence
  27842. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  27843. *
  27844. * @type {boolean}
  27845. * @since 5.0.0
  27846. * @product highstock
  27847. * @apioption plotOptions.series.showInNavigator
  27848. */
  27849. /**
  27850. * If set to `true`, the accessibility module will skip past the points
  27851. * in this series for keyboard navigation.
  27852. *
  27853. * @type {boolean}
  27854. * @since 5.0.12
  27855. * @apioption plotOptions.series.skipKeyboardNavigation
  27856. */
  27857. /**
  27858. * Whether to stack the values of each series on top of each other.
  27859. * Possible values are `undefined` to disable, `"normal"` to stack by
  27860. * value or `"percent"`. When stacking is enabled, data must be sorted
  27861. * in ascending X order. A special stacking option is with the
  27862. * streamgraph series type, where the stacking option is set to
  27863. * `"stream"`. The second one is `"overlap"`, which only applies to
  27864. * waterfall series.
  27865. *
  27866. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  27867. *
  27868. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  27869. * Line
  27870. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  27871. * Column
  27872. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  27873. * Bar
  27874. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  27875. * Area
  27876. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  27877. * Line
  27878. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  27879. * Column
  27880. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  27881. * Bar
  27882. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  27883. * Area
  27884. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  27885. * Waterfall with normal stacking
  27886. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  27887. * Waterfall with overlap stacking
  27888. * @sample {highstock} stock/plotoptions/stacking/
  27889. * Area
  27890. *
  27891. * @type {string}
  27892. * @product highcharts highstock
  27893. * @validvalue ["normal", "overlap", "percent", "stream"]
  27894. * @apioption plotOptions.series.stacking
  27895. */
  27896. /**
  27897. * Whether to apply steps to the line. Possible values are `left`,
  27898. * `center` and `right`.
  27899. *
  27900. * @sample {highcharts} highcharts/plotoptions/line-step/
  27901. * Different step line options
  27902. * @sample {highcharts} highcharts/plotoptions/area-step/
  27903. * Stepped, stacked area
  27904. * @sample {highstock} stock/plotoptions/line-step/
  27905. * Step line
  27906. *
  27907. * @type {string}
  27908. * @since 1.2.5
  27909. * @product highcharts highstock
  27910. * @validvalue ["left", "center", "right"]
  27911. * @apioption plotOptions.series.step
  27912. */
  27913. /**
  27914. * The threshold, also called zero level or base level. For line type
  27915. * series this is only used in conjunction with
  27916. * [negativeColor](#plotOptions.series.negativeColor).
  27917. *
  27918. * @see [softThreshold](#plotOptions.series.softThreshold).
  27919. *
  27920. * @type {number}
  27921. * @default 0
  27922. * @since 3.0
  27923. * @product highcharts highstock
  27924. * @apioption plotOptions.series.threshold
  27925. */
  27926. /**
  27927. * Set the initial visibility of the series.
  27928. *
  27929. * @sample {highcharts} highcharts/plotoptions/series-visible/
  27930. * Two series, one hidden and one visible
  27931. * @sample {highstock} stock/plotoptions/series-visibility/
  27932. * Hidden series
  27933. *
  27934. * @type {boolean}
  27935. * @default true
  27936. * @apioption plotOptions.series.visible
  27937. */
  27938. /**
  27939. * Defines the Axis on which the zones are applied.
  27940. *
  27941. * @see [zones](#plotOptions.series.zones)
  27942. *
  27943. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  27944. * Zones on the X-Axis
  27945. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  27946. * Zones on the X-Axis
  27947. *
  27948. * @type {string}
  27949. * @default y
  27950. * @since 4.1.0
  27951. * @product highcharts highstock
  27952. * @apioption plotOptions.series.zoneAxis
  27953. */
  27954. /**
  27955. * General event handlers for the series items. These event hooks can
  27956. * also be attached to the series at run time using the
  27957. * `Highcharts.addEvent` function.
  27958. *
  27959. * @declare Highcharts.SeriesEventsOptionsObject
  27960. *
  27961. * @private
  27962. */
  27963. events: {},
  27964. /**
  27965. * Fires after the series has finished its initial animation, or in case
  27966. * animation is disabled, immediately as the series is displayed.
  27967. *
  27968. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  27969. * Show label after animate
  27970. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  27971. * Show label after animate
  27972. *
  27973. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  27974. * @since 4.0
  27975. * @product highcharts highstock gantt
  27976. * @context Highcharts.Series
  27977. * @apioption plotOptions.series.events.afterAnimate
  27978. */
  27979. /**
  27980. * Fires when the checkbox next to the series' name in the legend is
  27981. * clicked. One parameter, `event`, is passed to the function. The state
  27982. * of the checkbox is found by `event.checked`. The checked item is
  27983. * found by `event.item`. Return `false` to prevent the default action
  27984. * which is to toggle the select state of the series.
  27985. *
  27986. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  27987. * Alert checkbox status
  27988. *
  27989. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  27990. * @since 1.2.0
  27991. * @context Highcharts.Series
  27992. * @apioption plotOptions.series.events.checkboxClick
  27993. */
  27994. /**
  27995. * Fires when the series is clicked. One parameter, `event`, is passed
  27996. * to the function, containing common event information. Additionally,
  27997. * `event.point` holds a pointer to the nearest point on the graph.
  27998. *
  27999. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  28000. * Alert click info
  28001. * @sample {highstock} stock/plotoptions/series-events-click/
  28002. * Alert click info
  28003. * @sample {highmaps} maps/plotoptions/series-events-click/
  28004. * Display click info in subtitle
  28005. *
  28006. * @type {Highcharts.SeriesClickCallbackFunction}
  28007. * @context Highcharts.Series
  28008. * @apioption plotOptions.series.events.click
  28009. */
  28010. /**
  28011. * Fires when the series is hidden after chart generation time, either
  28012. * by clicking the legend item or by calling `.hide()`.
  28013. *
  28014. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  28015. * Alert when the series is hidden by clicking the legend item
  28016. *
  28017. * @type {Highcharts.SeriesHideCallbackFunction}
  28018. * @since 1.2.0
  28019. * @context Highcharts.Series
  28020. * @apioption plotOptions.series.events.hide
  28021. */
  28022. /**
  28023. * Fires when the legend item belonging to the series is clicked. One
  28024. * parameter, `event`, is passed to the function. The default action
  28025. * is to toggle the visibility of the series. This can be prevented
  28026. * by returning `false` or calling `event.preventDefault()`.
  28027. *
  28028. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  28029. * Confirm hiding and showing
  28030. *
  28031. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  28032. * @context Highcharts.Series
  28033. * @apioption plotOptions.series.events.legendItemClick
  28034. */
  28035. /**
  28036. * Fires when the mouse leaves the graph. One parameter, `event`, is
  28037. * passed to the function, containing common event information. If the
  28038. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  28039. * doesn't happen before the mouse enters another graph or leaves the
  28040. * plot area.
  28041. *
  28042. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  28043. * With sticky tracking by default
  28044. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  28045. * Without sticky tracking
  28046. *
  28047. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  28048. * @context Highcharts.Series
  28049. * @apioption plotOptions.series.events.mouseOut
  28050. */
  28051. /**
  28052. * Fires when the mouse enters the graph. One parameter, `event`, is
  28053. * passed to the function, containing common event information.
  28054. *
  28055. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  28056. * With sticky tracking by default
  28057. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  28058. * Without sticky tracking
  28059. *
  28060. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  28061. * @context Highcharts.Series
  28062. * @apioption plotOptions.series.events.mouseOver
  28063. */
  28064. /**
  28065. * Fires when the series is shown after chart generation time, either
  28066. * by clicking the legend item or by calling `.show()`.
  28067. *
  28068. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  28069. * Alert when the series is shown by clicking the legend item.
  28070. *
  28071. * @type {Highcharts.SeriesShowCallbackFunction}
  28072. * @since 1.2.0
  28073. * @context Highcharts.Series
  28074. * @apioption plotOptions.series.events.show
  28075. */
  28076. /**
  28077. * Options for the point markers of line-like series. Properties like
  28078. * `fillColor`, `lineColor` and `lineWidth` define the visual appearance
  28079. * of the markers. Other series types, like column series, don't have
  28080. * markers, but have visual options on the series level instead.
  28081. *
  28082. * In styled mode, the markers can be styled with the
  28083. * `.highcharts-point`, `.highcharts-point-hover` and
  28084. * `.highcharts-point-select` class names.
  28085. *
  28086. * @declare Highcharts.PointMarkerOptionsObject
  28087. *
  28088. * @private
  28089. */
  28090. marker: {
  28091. /**
  28092. * Enable or disable the point marker. If `undefined`, the markers
  28093. * are hidden when the data is dense, and shown for more widespread
  28094. * data points.
  28095. *
  28096. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  28097. * Disabled markers
  28098. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  28099. * Disabled in normal state but enabled on hover
  28100. * @sample {highstock} stock/plotoptions/series-marker/
  28101. * Enabled markers
  28102. *
  28103. * @type {boolean}
  28104. * @default {highcharts} undefined
  28105. * @default {highstock} false
  28106. * @apioption plotOptions.series.marker.enabled
  28107. */
  28108. /**
  28109. * The threshold for how dense the point markers should be before
  28110. * they are hidden, given that `enabled` is not defined. The number
  28111. * indicates the horizontal distance between the two closest points
  28112. * in the series, as multiples of the `marker.radius`. In other
  28113. * words, the default value of 2 means points are hidden if
  28114. * overlapping horizontally.
  28115. *
  28116. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  28117. * A higher threshold
  28118. *
  28119. * @since 6.0.5
  28120. */
  28121. enabledThreshold: 2,
  28122. /**
  28123. * The fill color of the point marker. When `undefined`, the series'
  28124. * or point's color is used.
  28125. *
  28126. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28127. * White fill
  28128. *
  28129. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28130. * @apioption plotOptions.series.marker.fillColor
  28131. */
  28132. /**
  28133. * Image markers only. Set the image width explicitly. When using
  28134. * this option, a `width` must also be set.
  28135. *
  28136. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  28137. * Fixed width and height
  28138. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  28139. * Fixed width and height
  28140. *
  28141. * @type {number}
  28142. * @since 4.0.4
  28143. * @apioption plotOptions.series.marker.height
  28144. */
  28145. /**
  28146. * The color of the point marker's outline. When `undefined`, the
  28147. * series' or point's color is used.
  28148. *
  28149. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28150. * Inherit from series color (undefined)
  28151. *
  28152. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28153. */
  28154. lineColor: '#ffffff',
  28155. /**
  28156. * The width of the point marker's outline.
  28157. *
  28158. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28159. * 2px blue marker
  28160. */
  28161. lineWidth: 0,
  28162. /**
  28163. * The radius of the point marker.
  28164. *
  28165. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  28166. * Bigger markers
  28167. *
  28168. * @default {highstock} 2
  28169. */
  28170. radius: 4,
  28171. /**
  28172. * A predefined shape or symbol for the marker. When undefined, the
  28173. * symbol is pulled from options.symbols. Other possible values are
  28174. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  28175. * `'triangle-down'`.
  28176. *
  28177. * Additionally, the URL to a graphic can be given on this form:
  28178. * `'url(graphic.png)'`. Note that for the image to be applied to
  28179. * exported charts, its URL needs to be accessible by the export
  28180. * server.
  28181. *
  28182. * Custom callbacks for symbol path generation can also be added to
  28183. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  28184. * used by its method name, as shown in the demo.
  28185. *
  28186. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  28187. * Predefined, graphic and custom markers
  28188. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  28189. * Predefined, graphic and custom markers
  28190. *
  28191. * @type {string}
  28192. * @apioption plotOptions.series.marker.symbol
  28193. */
  28194. /**
  28195. * Image markers only. Set the image width explicitly. When using
  28196. * this option, a `height` must also be set.
  28197. *
  28198. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  28199. * Fixed width and height
  28200. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  28201. * Fixed width and height
  28202. *
  28203. * @type {number}
  28204. * @since 4.0.4
  28205. * @apioption plotOptions.series.marker.width
  28206. */
  28207. /**
  28208. * States for a single point marker.
  28209. *
  28210. * @declare Highcharts.PointStatesOptionsObject
  28211. */
  28212. states: {
  28213. /**
  28214. * The normal state of a single point marker. Currently only
  28215. * used for setting animation when returning to normal state
  28216. * from hover.
  28217. *
  28218. * @declare Highcharts.PointStatesNormalOptionsObject
  28219. */
  28220. normal: {
  28221. /**
  28222. * Animation when returning to normal state after hovering.
  28223. *
  28224. * @type {boolean|Highcharts.AnimationOptionsObject}
  28225. */
  28226. animation: true
  28227. },
  28228. /**
  28229. * The hover state for a single point marker.
  28230. *
  28231. * @declare Highcharts.PointStatesHoverOptionsObject
  28232. */
  28233. hover: {
  28234. /**
  28235. * Animation when hovering over the marker.
  28236. *
  28237. * @type {boolean|Highcharts.AnimationOptionsObject}
  28238. */
  28239. animation: {
  28240. /** @internal */
  28241. duration: 50
  28242. },
  28243. /**
  28244. * Enable or disable the point marker.
  28245. *
  28246. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  28247. * Disabled hover state
  28248. */
  28249. enabled: true,
  28250. /**
  28251. * The fill color of the marker in hover state. When
  28252. * `undefined`, the series' or point's fillColor for normal
  28253. * state is used.
  28254. *
  28255. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28256. * @apioption plotOptions.series.marker.states.hover.fillColor
  28257. */
  28258. /**
  28259. * The color of the point marker's outline. When
  28260. * `undefined`, the series' or point's lineColor for normal
  28261. * state is used.
  28262. *
  28263. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  28264. * White fill color, black line color
  28265. *
  28266. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28267. * @apioption plotOptions.series.marker.states.hover.lineColor
  28268. */
  28269. /**
  28270. * The width of the point marker's outline. When
  28271. * `undefined`, the series' or point's lineWidth for normal
  28272. * state is used.
  28273. *
  28274. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  28275. * 3px line width
  28276. *
  28277. * @type {number}
  28278. * @apioption plotOptions.series.marker.states.hover.lineWidth
  28279. */
  28280. /**
  28281. * The radius of the point marker. In hover state, it
  28282. * defaults to the normal state's radius + 2 as per the
  28283. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  28284. * option.
  28285. *
  28286. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  28287. * 10px radius
  28288. *
  28289. * @type {number}
  28290. * @apioption plotOptions.series.marker.states.hover.radius
  28291. */
  28292. /**
  28293. * The number of pixels to increase the radius of the
  28294. * hovered point.
  28295. *
  28296. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  28297. * 5 pixels greater radius on hover
  28298. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  28299. * 5 pixels greater radius on hover
  28300. *
  28301. * @since 4.0.3
  28302. */
  28303. radiusPlus: 2,
  28304. /**
  28305. * The additional line width for a hovered point.
  28306. *
  28307. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  28308. * 2 pixels wider on hover
  28309. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  28310. * 2 pixels wider on hover
  28311. *
  28312. * @since 4.0.3
  28313. */
  28314. lineWidthPlus: 1
  28315. },
  28316. /**
  28317. * The appearance of the point marker when selected. In order to
  28318. * allow a point to be selected, set the
  28319. * `series.allowPointSelect` option to true.
  28320. *
  28321. * @declare Highcharts.PointStatesSelectOptionsObject
  28322. */
  28323. select: {
  28324. /**
  28325. * Enable or disable visible feedback for selection.
  28326. *
  28327. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  28328. * Disabled select state
  28329. *
  28330. * @type {boolean}
  28331. * @default true
  28332. * @apioption plotOptions.series.marker.states.select.enabled
  28333. */
  28334. /**
  28335. * The radius of the point marker. In hover state, it
  28336. * defaults to the normal state's radius + 2.
  28337. *
  28338. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  28339. * 10px radius for selected points
  28340. *
  28341. * @type {number}
  28342. * @apioption plotOptions.series.marker.states.select.radius
  28343. */
  28344. /**
  28345. * The fill color of the point marker.
  28346. *
  28347. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  28348. * Solid red discs for selected points
  28349. *
  28350. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28351. */
  28352. fillColor: '#cccccc',
  28353. /**
  28354. * The color of the point marker's outline. When
  28355. * `undefined`, the series' or point's color is used.
  28356. *
  28357. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  28358. * Red line color for selected points
  28359. *
  28360. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28361. */
  28362. lineColor: '#000000',
  28363. /**
  28364. * The width of the point marker's outline.
  28365. *
  28366. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  28367. * 3px line width for selected points
  28368. */
  28369. lineWidth: 2
  28370. }
  28371. }
  28372. },
  28373. /**
  28374. * Properties for each single point.
  28375. *
  28376. * @declare Highcharts.PlotSeriesPointOptions
  28377. *
  28378. * @private
  28379. */
  28380. point: {
  28381. /**
  28382. * Fires when a point is clicked. One parameter, `event`, is passed
  28383. * to the function, containing common event information.
  28384. *
  28385. * If the `series.allowPointSelect` option is true, the default
  28386. * action for the point's click event is to toggle the point's
  28387. * select state. Returning `false` cancels this action.
  28388. *
  28389. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  28390. * Click marker to alert values
  28391. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  28392. * Click column
  28393. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  28394. * Go to URL
  28395. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  28396. * Click marker to display values
  28397. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  28398. * Go to URL
  28399. *
  28400. * @type {Highcharts.PointClickCallbackFunction}
  28401. * @context Highcharts.Point
  28402. * @apioption plotOptions.series.point.events.click
  28403. */
  28404. /**
  28405. * Fires when the mouse leaves the area close to the point. One
  28406. * parameter, `event`, is passed to the function, containing common
  28407. * event information.
  28408. *
  28409. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  28410. * Show values in the chart's corner on mouse over
  28411. *
  28412. * @type {Highcharts.PointMouseOutCallbackFunction}
  28413. * @context Highcharts.Point
  28414. * @apioption plotOptions.series.point.events.mouseOut
  28415. */
  28416. /**
  28417. * Fires when the mouse enters the area close to the point. One
  28418. * parameter, `event`, is passed to the function, containing common
  28419. * event information.
  28420. *
  28421. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  28422. * Show values in the chart's corner on mouse over
  28423. *
  28424. * @type {Highcharts.PointMouseOverCallbackFunction}
  28425. * @context Highcharts.Point
  28426. * @apioption plotOptions.series.point.events.mouseOver
  28427. */
  28428. /**
  28429. * Fires when the point is removed using the `.remove()` method. One
  28430. * parameter, `event`, is passed to the function. Returning `false`
  28431. * cancels the operation.
  28432. *
  28433. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  28434. * Remove point and confirm
  28435. *
  28436. * @type {Highcharts.PointRemoveCallbackFunction}
  28437. * @since 1.2.0
  28438. * @context Highcharts.Point
  28439. * @apioption plotOptions.series.point.events.remove
  28440. */
  28441. /**
  28442. * Fires when the point is selected either programmatically or
  28443. * following a click on the point. One parameter, `event`, is passed
  28444. * to the function. Returning `false` cancels the operation.
  28445. *
  28446. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  28447. * Report the last selected point
  28448. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28449. * Report select and unselect
  28450. *
  28451. * @type {Highcharts.PointSelectCallbackFunction}
  28452. * @since 1.2.0
  28453. * @context Highcharts.Point
  28454. * @apioption plotOptions.series.point.events.select
  28455. */
  28456. /**
  28457. * Fires when the point is unselected either programmatically or
  28458. * following a click on the point. One parameter, `event`, is passed
  28459. * to the function.
  28460. * Returning `false` cancels the operation.
  28461. *
  28462. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  28463. * Report the last unselected point
  28464. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28465. * Report select and unselect
  28466. *
  28467. * @type {Highcharts.PointUnselectCallbackFunction}
  28468. * @since 1.2.0
  28469. * @context Highcharts.Point
  28470. * @apioption plotOptions.series.point.events.unselect
  28471. */
  28472. /**
  28473. * Fires when the point is updated programmatically through the
  28474. * `.update()` method. One parameter, `event`, is passed to the
  28475. * function. The new point options can be accessed through
  28476. * `event.options`. Returning `false` cancels the operation.
  28477. *
  28478. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  28479. * Confirm point updating
  28480. *
  28481. * @type {Highcharts.PointUpdateCallbackFunction}
  28482. * @since 1.2.0
  28483. * @context Highcharts.Point
  28484. * @apioption plotOptions.series.point.events.update
  28485. */
  28486. /**
  28487. * Events for each single point.
  28488. *
  28489. * @declare Highcharts.PointEventsOptionsObject
  28490. */
  28491. events: {}
  28492. },
  28493. /**
  28494. * Options for the series data labels, appearing next to each data
  28495. * point.
  28496. *
  28497. * Since v6.2.0, multiple data labels can be applied to each single
  28498. * point by defining them as an array of configs.
  28499. *
  28500. * In styled mode, the data labels can be styled with the
  28501. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  28502. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  28503. *
  28504. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  28505. * Data labels enabled
  28506. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  28507. * Multiple data labels on a bar series
  28508. * @sample {highcharts} highcharts/css/series-datalabels
  28509. * Style mode example
  28510. *
  28511. * @declare Highcharts.DataLabelsOptionsObject
  28512. * @type {*|Array<*>}
  28513. * @product highcharts highstock highmaps gantt
  28514. *
  28515. * @private
  28516. */
  28517. dataLabels: {
  28518. /**
  28519. * The alignment of the data label compared to the point. If
  28520. * `right`, the right side of the label should be touching the
  28521. * point. For points with an extent, like columns, the alignments
  28522. * also dictates how to align it inside the box, as given with the
  28523. * [inside](#plotOptions.column.dataLabels.inside)
  28524. * option. Can be one of `left`, `center` or `right`.
  28525. *
  28526. * @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
  28527. * Left aligned
  28528. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  28529. * Data labels inside the bar
  28530. *
  28531. * @type {Highcharts.AlignValue|null}
  28532. */
  28533. align: 'center',
  28534. /**
  28535. * Whether to allow data labels to overlap. To make the labels less
  28536. * sensitive for overlapping, the
  28537. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  28538. * can be set to 0.
  28539. *
  28540. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  28541. * Don't allow overlap
  28542. *
  28543. * @type {boolean}
  28544. * @default false
  28545. * @since 4.1.0
  28546. * @apioption plotOptions.series.dataLabels.allowOverlap
  28547. */
  28548. /**
  28549. * The background color or gradient for the data label.
  28550. *
  28551. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28552. * Data labels box options
  28553. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  28554. * Data labels box options
  28555. *
  28556. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28557. * @since 2.2.1
  28558. * @apioption plotOptions.series.dataLabels.backgroundColor
  28559. */
  28560. /**
  28561. * The border color for the data label. Defaults to `undefined`.
  28562. *
  28563. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28564. * Data labels box options
  28565. *
  28566. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28567. * @since 2.2.1
  28568. * @apioption plotOptions.series.dataLabels.borderColor
  28569. */
  28570. /**
  28571. * The border radius in pixels for the data label.
  28572. *
  28573. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28574. * Data labels box options
  28575. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  28576. * Data labels box options
  28577. *
  28578. * @type {number}
  28579. * @default 0
  28580. * @since 2.2.1
  28581. * @apioption plotOptions.series.dataLabels.borderRadius
  28582. */
  28583. /**
  28584. * The border width in pixels for the data label.
  28585. *
  28586. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28587. * Data labels box options
  28588. *
  28589. * @type {number}
  28590. * @default 0
  28591. * @since 2.2.1
  28592. * @apioption plotOptions.series.dataLabels.borderWidth
  28593. */
  28594. /**
  28595. * A class name for the data label. Particularly in styled mode,
  28596. * this can be used to give each series' or point's data label
  28597. * unique styling. In addition to this option, a default color class
  28598. * name is added so that we can give the labels a contrast text
  28599. * shadow.
  28600. *
  28601. * @sample {highcharts} highcharts/css/data-label-contrast/
  28602. * Contrast text shadow
  28603. * @sample {highcharts} highcharts/css/series-datalabels/
  28604. * Styling by CSS
  28605. *
  28606. * @type {string}
  28607. * @since 5.0.0
  28608. * @apioption plotOptions.series.dataLabels.className
  28609. */
  28610. /**
  28611. * The text color for the data labels. Defaults to `undefined`. For
  28612. * certain series types, like column or map, the data labels can be
  28613. * drawn inside the points. In this case the data label will be
  28614. * drawn with maximum contrast by default. Additionally, it will be
  28615. * given a `text-outline` style with the opposite color, to further
  28616. * increase the contrast. This can be overridden by setting the
  28617. * `text-outline` style to `none` in the `dataLabels.style` option.
  28618. *
  28619. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  28620. * Red data labels
  28621. * @sample {highmaps} maps/demo/color-axis/
  28622. * White data labels
  28623. *
  28624. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28625. * @apioption plotOptions.series.dataLabels.color
  28626. */
  28627. /**
  28628. * Whether to hide data labels that are outside the plot area. By
  28629. * default, the data label is moved inside the plot area according
  28630. * to the
  28631. * [overflow](#plotOptions.series.dataLabels.overflow)
  28632. * option.
  28633. *
  28634. * @type {boolean}
  28635. * @default true
  28636. * @since 2.3.3
  28637. * @apioption plotOptions.series.dataLabels.crop
  28638. */
  28639. /**
  28640. * Whether to defer displaying the data labels until the initial
  28641. * series animation has finished.
  28642. *
  28643. * @type {boolean}
  28644. * @default true
  28645. * @since 4.0.0
  28646. * @product highcharts highstock gantt
  28647. * @apioption plotOptions.series.dataLabels.defer
  28648. */
  28649. /**
  28650. * Enable or disable the data labels.
  28651. *
  28652. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  28653. * Data labels enabled
  28654. * @sample {highmaps} maps/demo/color-axis/
  28655. * Data labels enabled
  28656. *
  28657. * @type {boolean}
  28658. * @default false
  28659. * @apioption plotOptions.series.dataLabels.enabled
  28660. */
  28661. /**
  28662. * A declarative filter to control of which data labels to display.
  28663. * The declarative filter is designed for use when callback
  28664. * functions are not available, like when the chart options require
  28665. * a pure JSON structure or for use with graphical editors. For
  28666. * programmatic control, use the `formatter` instead, and return
  28667. * `undefined` to disable a single data label.
  28668. *
  28669. * @example
  28670. * filter: {
  28671. * property: 'percentage',
  28672. * operator: '>',
  28673. * value: 4
  28674. * }
  28675. *
  28676. * @sample {highcharts} highcharts/demo/pie-monochrome
  28677. * Data labels filtered by percentage
  28678. *
  28679. * @declare Highcharts.DataLabelsFilterOptionsObject
  28680. * @since 6.0.3
  28681. * @apioption plotOptions.series.dataLabels.filter
  28682. */
  28683. /**
  28684. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  28685. * `==`, and `===`.
  28686. *
  28687. * @type {string}
  28688. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  28689. * @apioption plotOptions.series.dataLabels.filter.operator
  28690. */
  28691. /**
  28692. * The point property to filter by. Point options are passed
  28693. * directly to properties, additionally there are `y` value,
  28694. * `percentage` and others listed under {@link Highcharts.Point}
  28695. * members.
  28696. *
  28697. * @type {string}
  28698. * @apioption plotOptions.series.dataLabels.filter.property
  28699. */
  28700. /**
  28701. * The value to compare against.
  28702. *
  28703. * @type {number}
  28704. * @apioption plotOptions.series.dataLabels.filter.value
  28705. */
  28706. /**
  28707. * A
  28708. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  28709. * for the data label. Available variables are the same as for
  28710. * `formatter`.
  28711. *
  28712. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  28713. * Add a unit
  28714. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  28715. * Formatted value in the data label
  28716. *
  28717. * @type {string}
  28718. * @default y
  28719. * @default point.value
  28720. * @since 3.0
  28721. * @apioption plotOptions.series.dataLabels.format
  28722. */
  28723. // eslint-disable-next-line valid-jsdoc
  28724. /**
  28725. * Callback JavaScript function to format the data label. Note that
  28726. * if a `format` is defined, the format takes precedence and the
  28727. * formatter is ignored.
  28728. *
  28729. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  28730. * Formatted value
  28731. *
  28732. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  28733. */
  28734. formatter: function () {
  28735. var numberFormatter = this.series.chart.numberFormatter;
  28736. return this.y === null ? '' : numberFormatter(this.y, -1);
  28737. },
  28738. /**
  28739. * For points with an extent, like columns or map areas, whether to
  28740. * align the data label inside the box or to the actual value point.
  28741. * Defaults to `false` in most cases, `true` in stacked columns.
  28742. *
  28743. * @type {boolean}
  28744. * @since 3.0
  28745. * @apioption plotOptions.series.dataLabels.inside
  28746. */
  28747. /**
  28748. * Format for points with the value of null. Works analogously to
  28749. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  28750. * be applied only to series which support displaying null points.
  28751. *
  28752. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  28753. * Format data label and tooltip for null point.
  28754. *
  28755. * @type {boolean|string}
  28756. * @since 7.1.0
  28757. * @apioption plotOptions.series.dataLabels.nullFormat
  28758. */
  28759. /**
  28760. * Callback JavaScript function that defines formatting for points
  28761. * with the value of null. Works analogously to
  28762. * [formatter](#plotOptions.series.dataLabels.formatter).
  28763. * `nullPointFormatter` can be applied only to series which support
  28764. * displaying null points.
  28765. *
  28766. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  28767. * Format data label and tooltip for null point.
  28768. *
  28769. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  28770. * @since 7.1.0
  28771. * @apioption plotOptions.series.dataLabels.nullFormatter
  28772. */
  28773. /**
  28774. * How to handle data labels that flow outside the plot area. The
  28775. * default is `"justify"`, which aligns them inside the plot area.
  28776. * For columns and bars, this means it will be moved inside the bar.
  28777. * To display data labels outside the plot area, set `crop` to
  28778. * `false` and `overflow` to `"allow"`.
  28779. *
  28780. * @type {Highcharts.DataLabelsOverflowValue}
  28781. * @default justify
  28782. * @since 3.0.6
  28783. * @apioption plotOptions.series.dataLabels.overflow
  28784. */
  28785. /**
  28786. * When either the `borderWidth` or the `backgroundColor` is set,
  28787. * this is the padding within the box.
  28788. *
  28789. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28790. * Data labels box options
  28791. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  28792. * Data labels box options
  28793. *
  28794. * @since 2.2.1
  28795. */
  28796. padding: 5,
  28797. /**
  28798. * Aligns data labels relative to points. If `center` alignment is
  28799. * not possible, it defaults to `right`.
  28800. *
  28801. * @type {Highcharts.AlignValue}
  28802. * @default center
  28803. * @apioption plotOptions.series.dataLabels.position
  28804. */
  28805. /**
  28806. * Text rotation in degrees. Note that due to a more complex
  28807. * structure, backgrounds, borders and padding will be lost on a
  28808. * rotated data label.
  28809. *
  28810. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  28811. * Vertical labels
  28812. *
  28813. * @type {number}
  28814. * @default 0
  28815. * @apioption plotOptions.series.dataLabels.rotation
  28816. */
  28817. /**
  28818. * The shadow of the box. Works best with `borderWidth` or
  28819. * `backgroundColor`. Since 2.3 the shadow can be an object
  28820. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  28821. * and `width`.
  28822. *
  28823. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28824. * Data labels box options
  28825. *
  28826. * @type {boolean|Highcharts.ShadowOptionsObject}
  28827. * @default false
  28828. * @since 2.2.1
  28829. * @apioption plotOptions.series.dataLabels.shadow
  28830. */
  28831. /**
  28832. * The name of a symbol to use for the border around the label.
  28833. * Symbols are predefined functions on the Renderer object.
  28834. *
  28835. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  28836. * A callout for annotations
  28837. *
  28838. * @type {string}
  28839. * @default square
  28840. * @since 4.1.2
  28841. * @apioption plotOptions.series.dataLabels.shape
  28842. */
  28843. /**
  28844. * Styles for the label. The default `color` setting is
  28845. * `"contrast"`, which is a pseudo color that Highcharts picks up
  28846. * and applies the maximum contrast to the underlying point item,
  28847. * for example the bar in a bar chart.
  28848. *
  28849. * The `textOutline` is a pseudo property that applies an outline of
  28850. * the given width with the given color, which by default is the
  28851. * maximum contrast to the text. So a bright text color will result
  28852. * in a black text outline for maximum readability on a mixed
  28853. * background. In some cases, especially with grayscale text, the
  28854. * text outline doesn't work well, in which cases it can be disabled
  28855. * by setting it to `"none"`. When `useHTML` is true, the
  28856. * `textOutline` will not be picked up. In this, case, the same
  28857. * effect can be acheived through the `text-shadow` CSS property.
  28858. *
  28859. * For some series types, where each point has an extent, like for
  28860. * example tree maps, the data label may overflow the point. There
  28861. * are two strategies for handling overflow. By default, the text
  28862. * will wrap to multiple lines. The other strategy is to set
  28863. * `style.textOverflow` to `ellipsis`, which will keep the text on
  28864. * one line plus it will break inside long words.
  28865. *
  28866. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  28867. * Bold labels
  28868. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow/
  28869. * Long labels truncated with an ellipsis in a pie
  28870. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow-wrap/
  28871. * Long labels are wrapped in a pie
  28872. * @sample {highmaps} maps/demo/color-axis/
  28873. * Bold labels
  28874. *
  28875. * @type {Highcharts.CSSObject}
  28876. * @since 4.1.0
  28877. * @apioption plotOptions.series.dataLabels.style
  28878. */
  28879. style: {
  28880. /** @internal */
  28881. fontSize: '11px',
  28882. /** @internal */
  28883. fontWeight: 'bold',
  28884. /** @internal */
  28885. color: 'contrast',
  28886. /** @internal */
  28887. textOutline: '1px contrast'
  28888. },
  28889. /**
  28890. * Options for a label text which should follow marker's shape.
  28891. * Border and background are disabled for a label that follows a
  28892. * path.
  28893. *
  28894. * **Note:** Only SVG-based renderer supports this option. Setting
  28895. * `useHTML` to true will disable this option.
  28896. *
  28897. * @declare Highcharts.DataLabelsTextPathOptionsObject
  28898. * @since 7.1.0
  28899. * @apioption plotOptions.series.dataLabels.textPath
  28900. */
  28901. /**
  28902. * Presentation attributes for the text path.
  28903. *
  28904. * @type {Highcharts.SVGAttributes}
  28905. * @since 7.1.0
  28906. * @apioption plotOptions.series.dataLabels.textPath.attributes
  28907. */
  28908. /**
  28909. * Enable or disable `textPath` option for link's or marker's data
  28910. * labels.
  28911. *
  28912. * @type {boolean}
  28913. * @since 7.1.0
  28914. * @apioption plotOptions.series.dataLabels.textPath.enabled
  28915. */
  28916. /**
  28917. * Whether to
  28918. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  28919. * to render the labels.
  28920. *
  28921. * @type {boolean}
  28922. * @default false
  28923. * @apioption plotOptions.series.dataLabels.useHTML
  28924. */
  28925. /**
  28926. * The vertical alignment of a data label. Can be one of `top`,
  28927. * `middle` or `bottom`. The default value depends on the data, for
  28928. * instance in a column chart, the label is above positive values
  28929. * and below negative values.
  28930. *
  28931. * @type {Highcharts.VerticalAlignValue|null}
  28932. * @since 2.3.3
  28933. */
  28934. verticalAlign: 'bottom',
  28935. /**
  28936. * The x position offset of the label relative to the point in
  28937. * pixels.
  28938. *
  28939. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  28940. * Vertical and positioned
  28941. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  28942. * Data labels inside the bar
  28943. */
  28944. x: 0,
  28945. /**
  28946. * The Z index of the data labels. The default Z index puts it above
  28947. * the series. Use a Z index of 2 to display it behind the series.
  28948. *
  28949. * @type {number}
  28950. * @default 6
  28951. * @since 2.3.5
  28952. * @apioption plotOptions.series.dataLabels.z
  28953. */
  28954. /**
  28955. * The y position offset of the label relative to the point in
  28956. * pixels.
  28957. *
  28958. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  28959. * Vertical and positioned
  28960. */
  28961. y: 0
  28962. },
  28963. /**
  28964. * When the series contains less points than the crop threshold, all
  28965. * points are drawn, even if the points fall outside the visible plot
  28966. * area at the current zoom. The advantage of drawing all points
  28967. * (including markers and columns), is that animation is performed on
  28968. * updates. On the other hand, when the series contains more points than
  28969. * the crop threshold, the series data is cropped to only contain points
  28970. * that fall within the plot area. The advantage of cropping away
  28971. * invisible points is to increase performance on large series.
  28972. *
  28973. * @since 2.2
  28974. * @product highcharts highstock
  28975. *
  28976. * @private
  28977. */
  28978. cropThreshold: 300,
  28979. /**
  28980. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  28981. *
  28982. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  28983. *
  28984. * @since 7.1.0
  28985. *
  28986. * @private
  28987. */
  28988. opacity: 1,
  28989. /**
  28990. * The width of each point on the x axis. For example in a column chart
  28991. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  28992. * * 1000 milliseconds). This is normally computed automatically, but
  28993. * this option can be used to override the automatic value.
  28994. *
  28995. * @product highstock
  28996. *
  28997. * @private
  28998. */
  28999. pointRange: 0,
  29000. /**
  29001. * When this is true, the series will not cause the Y axis to cross
  29002. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  29003. * unless the data actually crosses the plane.
  29004. *
  29005. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  29006. * 3 will make the Y axis show negative values according to the
  29007. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  29008. * at 0.
  29009. *
  29010. * @since 4.1.9
  29011. * @product highcharts highstock
  29012. *
  29013. * @private
  29014. */
  29015. softThreshold: true,
  29016. /**
  29017. * @declare Highcharts.SeriesStatesOptionsObject
  29018. */
  29019. states: {
  29020. /**
  29021. * The normal state of a series, or for point items in column, pie
  29022. * and similar series. Currently only used for setting animation
  29023. * when returning to normal state from hover.
  29024. *
  29025. * @declare Highcharts.SeriesStatesNormalOptionsObject
  29026. */
  29027. normal: {
  29028. /**
  29029. * Animation when returning to normal state after hovering.
  29030. *
  29031. * @type {boolean|Highcharts.AnimationOptionsObject}
  29032. */
  29033. animation: true
  29034. },
  29035. /**
  29036. * Options for the hovered series. These settings override the
  29037. * normal state options when a series is moused over or touched.
  29038. *
  29039. * @declare Highcharts.SeriesStatesHoverOptionsObject
  29040. */
  29041. hover: {
  29042. /**
  29043. * Enable separate styles for the hovered series to visualize
  29044. * that the user hovers either the series itself or the legend.
  29045. *
  29046. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  29047. * Line
  29048. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  29049. * Column
  29050. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  29051. * Pie
  29052. *
  29053. * @type {boolean}
  29054. * @default true
  29055. * @since 1.2
  29056. * @apioption plotOptions.series.states.hover.enabled
  29057. */
  29058. /**
  29059. * Animation setting for hovering the graph in line-type series.
  29060. *
  29061. * @type {boolean|Highcharts.AnimationOptionsObject}
  29062. * @since 5.0.8
  29063. * @product highcharts highstock
  29064. */
  29065. animation: {
  29066. /**
  29067. * The duration of the hover animation in milliseconds. By
  29068. * default the hover state animates quickly in, and slowly
  29069. * back to normal.
  29070. *
  29071. * @internal
  29072. */
  29073. duration: 50
  29074. },
  29075. /**
  29076. * Pixel width of the graph line. By default this property is
  29077. * undefined, and the `lineWidthPlus` property dictates how much
  29078. * to increase the linewidth from normal state.
  29079. *
  29080. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  29081. * 5px line on hover
  29082. *
  29083. * @type {number}
  29084. * @product highcharts highstock
  29085. * @apioption plotOptions.series.states.hover.lineWidth
  29086. */
  29087. /**
  29088. * The additional line width for the graph of a hovered series.
  29089. *
  29090. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29091. * 5 pixels wider
  29092. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29093. * 5 pixels wider
  29094. *
  29095. * @since 4.0.3
  29096. * @product highcharts highstock
  29097. */
  29098. lineWidthPlus: 1,
  29099. /**
  29100. * In Highcharts 1.0, the appearance of all markers belonging
  29101. * to the hovered series. For settings on the hover state of the
  29102. * individual point, see
  29103. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  29104. *
  29105. * @deprecated
  29106. *
  29107. * @extends plotOptions.series.marker
  29108. * @excluding states
  29109. * @product highcharts highstock
  29110. */
  29111. marker: {
  29112. // lineWidth: base + 1,
  29113. // radius: base + 1
  29114. },
  29115. /**
  29116. * Options for the halo appearing around the hovered point in
  29117. * line-type series as well as outside the hovered slice in pie
  29118. * charts. By default the halo is filled by the current point or
  29119. * series color with an opacity of 0.25\. The halo can be
  29120. * disabled by setting the `halo` option to `null`.
  29121. *
  29122. * In styled mode, the halo is styled with the
  29123. * `.highcharts-halo` class, with colors inherited from
  29124. * `.highcharts-color-{n}`.
  29125. *
  29126. * @sample {highcharts} highcharts/plotoptions/halo/
  29127. * Halo options
  29128. * @sample {highstock} highcharts/plotoptions/halo/
  29129. * Halo options
  29130. *
  29131. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  29132. * @type {null|*}
  29133. * @since 4.0
  29134. * @product highcharts highstock
  29135. */
  29136. halo: {
  29137. /**
  29138. * A collection of SVG attributes to override the appearance
  29139. * of the halo, for example `fill`, `stroke` and
  29140. * `stroke-width`.
  29141. *
  29142. * @type {Highcharts.SVGAttributes}
  29143. * @since 4.0
  29144. * @product highcharts highstock
  29145. * @apioption plotOptions.series.states.hover.halo.attributes
  29146. */
  29147. /**
  29148. * The pixel size of the halo. For point markers this is the
  29149. * radius of the halo. For pie slices it is the width of the
  29150. * halo outside the slice. For bubbles it defaults to 5 and
  29151. * is the width of the halo outside the bubble.
  29152. *
  29153. * @since 4.0
  29154. * @product highcharts highstock
  29155. */
  29156. size: 10,
  29157. /**
  29158. * Opacity for the halo unless a specific fill is overridden
  29159. * using the `attributes` setting. Note that Highcharts is
  29160. * only able to apply opacity to colors of hex or rgb(a)
  29161. * formats.
  29162. *
  29163. * @since 4.0
  29164. * @product highcharts highstock
  29165. */
  29166. opacity: 0.25
  29167. }
  29168. },
  29169. /**
  29170. * Specific options for point in selected states, after being
  29171. * selected by
  29172. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  29173. * or programmatically.
  29174. *
  29175. * @sample maps/plotoptions/series-allowpointselect/
  29176. * Allow point select demo
  29177. *
  29178. * @declare Highcharts.SeriesStatesSelectOptionsObject
  29179. * @extends plotOptions.series.states.hover
  29180. * @excluding brightness
  29181. */
  29182. select: {
  29183. animation: {
  29184. /** @internal */
  29185. duration: 0
  29186. }
  29187. },
  29188. /**
  29189. * The opposite state of a hover for series.
  29190. *
  29191. * @sample highcharts/plotoptions/series-states-inactive-opacity
  29192. * Disabled inactive state by setting opacity
  29193. *
  29194. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  29195. */
  29196. inactive: {
  29197. /**
  29198. * The animation for entering the inactive state.
  29199. *
  29200. * @type {boolean|Highcharts.AnimationOptionsObject}
  29201. */
  29202. animation: {
  29203. /** @internal */
  29204. duration: 50
  29205. },
  29206. /**
  29207. * Opacity of series elements (dataLabels, line, area). Set to 1
  29208. * to disable inactive state.
  29209. *
  29210. * @apioption plotOptions.series.states.inactive.opacity
  29211. * @type {number}
  29212. * @sample highcharts/plotoptions/series-states-inactive-opacity
  29213. * Disabled inactive state
  29214. */
  29215. opacity: 0.2
  29216. }
  29217. },
  29218. /**
  29219. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  29220. * series isn't triggered until the mouse moves over another series, or
  29221. * out of the plot area. When false, the `mouseOut` event on a series is
  29222. * triggered when the mouse leaves the area around the series' graph or
  29223. * markers. This also implies the tooltip when not shared. When
  29224. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  29225. * will be hidden when moving the mouse between series. Defaults to true
  29226. * for line and area type series, but to false for columns, pies etc.
  29227. *
  29228. * **Note:** The boost module will force this option because of
  29229. * technical limitations.
  29230. *
  29231. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  29232. * True by default
  29233. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  29234. * False
  29235. *
  29236. * @default {highcharts} true
  29237. * @default {highstock} true
  29238. * @default {highmaps} false
  29239. * @since 2.0
  29240. *
  29241. * @private
  29242. */
  29243. stickyTracking: true,
  29244. /**
  29245. * A configuration object for the tooltip rendering of each single
  29246. * series. Properties are inherited from [tooltip](#tooltip), but only
  29247. * the following properties can be defined on a series level.
  29248. *
  29249. * @declare Highcharts.SeriesTooltipOptionsObject
  29250. * @since 2.3
  29251. * @extends tooltip
  29252. * @excluding animation, backgroundColor, borderColor, borderRadius,
  29253. * borderWidth, className, crosshairs, enabled, formatter,
  29254. * headerShape, hideDelay, outside, padding, positioner,
  29255. * shadow, shape, shared, snap, split, style, useHTML
  29256. * @apioption plotOptions.series.tooltip
  29257. */
  29258. /**
  29259. * When a series contains a data array that is longer than this, only
  29260. * one dimensional arrays of numbers, or two dimensional arrays with
  29261. * x and y values are allowed. Also, only the first point is tested,
  29262. * and the rest are assumed to be the same format. This saves expensive
  29263. * data checking and indexing in long series. Set it to `0` disable.
  29264. *
  29265. * Note:
  29266. * In boost mode turbo threshold is forced. Only array of numbers or
  29267. * two dimensional arrays are allowed.
  29268. *
  29269. * @since 2.2
  29270. * @product highcharts highstock gantt
  29271. *
  29272. * @private
  29273. */
  29274. turboThreshold: 1000,
  29275. /**
  29276. * An array defining zones within a series. Zones can be applied to the
  29277. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  29278. * option. The zone definitions have to be in ascending order regarding
  29279. * to the value.
  29280. *
  29281. * In styled mode, the color zones are styled with the
  29282. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  29283. * option
  29284. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  29285. *
  29286. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  29287. *
  29288. * @sample {highcharts} highcharts/series/color-zones-simple/
  29289. * Color zones
  29290. * @sample {highstock} highcharts/series/color-zones-simple/
  29291. * Color zones
  29292. *
  29293. * @declare Highcharts.SeriesZonesOptionsObject
  29294. * @type {Array<*>}
  29295. * @since 4.1.0
  29296. * @product highcharts highstock
  29297. * @apioption plotOptions.series.zones
  29298. */
  29299. /**
  29300. * Styled mode only. A custom class name for the zone.
  29301. *
  29302. * @sample highcharts/css/color-zones/
  29303. * Zones styled by class name
  29304. *
  29305. * @type {string}
  29306. * @since 5.0.0
  29307. * @apioption plotOptions.series.zones.className
  29308. */
  29309. /**
  29310. * Defines the color of the series.
  29311. *
  29312. * @see [series color](#plotOptions.series.color)
  29313. *
  29314. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29315. * @since 4.1.0
  29316. * @product highcharts highstock
  29317. * @apioption plotOptions.series.zones.color
  29318. */
  29319. /**
  29320. * A name for the dash style to use for the graph.
  29321. *
  29322. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  29323. *
  29324. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  29325. * Dashed line indicates prognosis
  29326. *
  29327. * @type {Highcharts.DashStyleValue}
  29328. * @since 4.1.0
  29329. * @product highcharts highstock
  29330. * @apioption plotOptions.series.zones.dashStyle
  29331. */
  29332. /**
  29333. * Defines the fill color for the series (in area type series)
  29334. *
  29335. * @see [fillColor](#plotOptions.area.fillColor)
  29336. *
  29337. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29338. * @since 4.1.0
  29339. * @product highcharts highstock
  29340. * @apioption plotOptions.series.zones.fillColor
  29341. */
  29342. /**
  29343. * The value up to where the zone extends, if undefined the zones
  29344. * stretches to the last value in the series.
  29345. *
  29346. * @type {number}
  29347. * @since 4.1.0
  29348. * @product highcharts highstock
  29349. * @apioption plotOptions.series.zones.value
  29350. */
  29351. /**
  29352. * When using dual or multiple color axes, this number defines which
  29353. * colorAxis the particular series is connected to. It refers to
  29354. * either the
  29355. * {@link #colorAxis.id|axis id}
  29356. * or the index of the axis in the colorAxis array, with 0 being the
  29357. * first. Set this option to false to prevent a series from connecting
  29358. * to the default color axis.
  29359. *
  29360. * Since v7.2.0 the option can also be an axis id or an axis index
  29361. * instead of a boolean flag.
  29362. *
  29363. * @sample highcharts/coloraxis/coloraxis-with-pie/
  29364. * Color axis with pie series
  29365. * @sample highcharts/coloraxis/multiple-coloraxis/
  29366. * Multiple color axis
  29367. *
  29368. * @type {number|string|boolean}
  29369. * @default 0
  29370. * @product highcharts highstock highmaps
  29371. * @apioption plotOptions.series.colorAxis
  29372. */
  29373. /**
  29374. * Determines what data value should be used to calculate point color
  29375. * if `colorAxis` is used. Requires to set `min` and `max` if some
  29376. * custom point property is used or if approximation for data grouping
  29377. * is set to `'sum'`.
  29378. *
  29379. * @sample highcharts/coloraxis/custom-color-key/
  29380. * Custom color key
  29381. * @sample highcharts/coloraxis/changed-default-color-key/
  29382. * Changed default color key
  29383. *
  29384. * @type {string}
  29385. * @default y
  29386. * @since 7.2.0
  29387. * @product highcharts highstock highmaps
  29388. * @apioption plotOptions.series.colorKey
  29389. */
  29390. /**
  29391. * Determines whether the series should look for the nearest point
  29392. * in both dimensions or just the x-dimension when hovering the series.
  29393. * Defaults to `'xy'` for scatter series and `'x'` for most other
  29394. * series. If the data has duplicate x-values, it is recommended to
  29395. * set this to `'xy'` to allow hovering over all points.
  29396. *
  29397. * Applies only to series types using nearest neighbor search (not
  29398. * direct hover) for tooltip.
  29399. *
  29400. * @sample {highcharts} highcharts/series/findnearestpointby/
  29401. * Different hover behaviors
  29402. * @sample {highstock} highcharts/series/findnearestpointby/
  29403. * Different hover behaviors
  29404. * @sample {highmaps} highcharts/series/findnearestpointby/
  29405. * Different hover behaviors
  29406. *
  29407. * @since 5.0.10
  29408. * @validvalue ["x", "xy"]
  29409. *
  29410. * @private
  29411. */
  29412. findNearestPointBy: 'x'
  29413. },
  29414. /* eslint-disable no-invalid-this, valid-jsdoc */
  29415. /** @lends Highcharts.Series.prototype */
  29416. {
  29417. axisTypes: ['xAxis', 'yAxis'],
  29418. coll: 'series',
  29419. colorCounter: 0,
  29420. cropShoulder: 1,
  29421. directTouch: false,
  29422. eventsToUnbind: [],
  29423. isCartesian: true,
  29424. // each point's x and y values are stored in this.xData and this.yData
  29425. parallelArrays: ['x', 'y'],
  29426. pointClass: Point,
  29427. requireSorting: true,
  29428. sorted: true,
  29429. init: function (chart, options) {
  29430. fireEvent(this, 'init', { options: options });
  29431. var series = this, events, chartSeries = chart.series, lastSeries;
  29432. // A lookup over those events that are added by _options_ (not
  29433. // programmatically). These are updated through Series.update()
  29434. // (#10861).
  29435. this.eventOptions = this.eventOptions || {};
  29436. /**
  29437. * Read only. The chart that the series belongs to.
  29438. *
  29439. * @name Highcharts.Series#chart
  29440. * @type {Highcharts.Chart}
  29441. */
  29442. series.chart = chart;
  29443. /**
  29444. * Read only. The series' type, like "line", "area", "column" etc.
  29445. * The type in the series options anc can be altered using
  29446. * {@link Series#update}.
  29447. *
  29448. * @name Highcharts.Series#type
  29449. * @type {string}
  29450. */
  29451. /**
  29452. * Read only. The series' current options. To update, use
  29453. * {@link Series#update}.
  29454. *
  29455. * @name Highcharts.Series#options
  29456. * @type {Highcharts.SeriesOptionsType}
  29457. */
  29458. series.options = options = series.setOptions(options);
  29459. series.linkedSeries = [];
  29460. // bind the axes
  29461. series.bindAxes();
  29462. // set some variables
  29463. extend(series, {
  29464. /**
  29465. * The series name as given in the options. Defaults to
  29466. * "Series {n}".
  29467. *
  29468. * @name Highcharts.Series#name
  29469. * @type {string}
  29470. */
  29471. name: options.name,
  29472. state: '',
  29473. /**
  29474. * Read only. The series' visibility state as set by {@link
  29475. * Series#show}, {@link Series#hide}, or in the initial
  29476. * configuration.
  29477. *
  29478. * @name Highcharts.Series#visible
  29479. * @type {boolean}
  29480. */
  29481. visible: options.visible !== false,
  29482. /**
  29483. * Read only. The series' selected state as set by {@link
  29484. * Highcharts.Series#select}.
  29485. *
  29486. * @name Highcharts.Series#selected
  29487. * @type {boolean}
  29488. */
  29489. selected: options.selected === true // false by default
  29490. });
  29491. // Register event listeners
  29492. events = options.events;
  29493. objectEach(events, function (event, eventType) {
  29494. if (H.isFunction(event)) {
  29495. // If event does not exist, or is changed by Series.update
  29496. if (series.eventOptions[eventType] !== event) {
  29497. // Remove existing if set by option
  29498. if (H.isFunction(series.eventOptions[eventType])) {
  29499. removeEvent(series, eventType, series.eventOptions[eventType]);
  29500. }
  29501. series.eventOptions[eventType] = event;
  29502. addEvent(series, eventType, event);
  29503. }
  29504. }
  29505. });
  29506. if ((events && events.click) ||
  29507. (options.point &&
  29508. options.point.events &&
  29509. options.point.events.click) ||
  29510. options.allowPointSelect) {
  29511. chart.runTrackerClick = true;
  29512. }
  29513. series.getColor();
  29514. series.getSymbol();
  29515. // Initialize the parallel data arrays
  29516. series.parallelArrays.forEach(function (key) {
  29517. if (!series[key + 'Data']) {
  29518. series[key + 'Data'] = [];
  29519. }
  29520. });
  29521. // Mark cartesian
  29522. if (series.isCartesian) {
  29523. chart.hasCartesianSeries = true;
  29524. }
  29525. // Get the index and register the series in the chart. The index is
  29526. // one more than the current latest series index (#5960).
  29527. if (chartSeries.length) {
  29528. lastSeries = chartSeries[chartSeries.length - 1];
  29529. }
  29530. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  29531. // Insert the series and re-order all series above the insertion
  29532. // point.
  29533. chart.orderSeries(this.insert(chartSeries));
  29534. // Set options for series with sorting and set data later.
  29535. if (options.dataSorting && options.dataSorting.enabled) {
  29536. series.setDataSortingOptions();
  29537. }
  29538. else if (!series.points && !series.data) {
  29539. series.setData(options.data, false);
  29540. }
  29541. fireEvent(this, 'afterInit');
  29542. },
  29543. /**
  29544. * Insert the series in a collection with other series, either the chart
  29545. * series or yAxis series, in the correct order according to the index
  29546. * option. Used internally when adding series.
  29547. *
  29548. * @private
  29549. * @function Highcharts.Series#insert
  29550. * @param {Array<Highcharts.Series>} collection
  29551. * A collection of series, like `chart.series` or `xAxis.series`.
  29552. * @return {number}
  29553. * The index of the series in the collection.
  29554. */
  29555. insert: function (collection) {
  29556. var indexOption = this.options.index, i;
  29557. // Insert by index option
  29558. if (isNumber(indexOption)) {
  29559. i = collection.length;
  29560. while (i--) {
  29561. // Loop down until the interted element has higher index
  29562. if (indexOption >=
  29563. pick(collection[i].options.index, collection[i]._i)) {
  29564. collection.splice(i + 1, 0, this);
  29565. break;
  29566. }
  29567. }
  29568. if (i === -1) {
  29569. collection.unshift(this);
  29570. }
  29571. i = i + 1;
  29572. // Or just push it to the end
  29573. }
  29574. else {
  29575. collection.push(this);
  29576. }
  29577. return pick(i, collection.length - 1);
  29578. },
  29579. /**
  29580. * Set the xAxis and yAxis properties of cartesian series, and register
  29581. * the series in the `axis.series` array.
  29582. *
  29583. * @private
  29584. * @function Highcharts.Series#bindAxes
  29585. * @return {void}
  29586. * @exception 18
  29587. */
  29588. bindAxes: function () {
  29589. var series = this, seriesOptions = series.options, chart = series.chart, axisOptions;
  29590. fireEvent(this, 'bindAxes', null, function () {
  29591. // repeat for xAxis and yAxis
  29592. (series.axisTypes || []).forEach(function (AXIS) {
  29593. // loop through the chart's axis objects
  29594. chart[AXIS].forEach(function (axis) {
  29595. axisOptions = axis.options;
  29596. // apply if the series xAxis or yAxis option mathches
  29597. // the number of the axis, or if undefined, use the
  29598. // first axis
  29599. if (seriesOptions[AXIS] ===
  29600. axisOptions.index ||
  29601. (typeof seriesOptions[AXIS] !==
  29602. 'undefined' &&
  29603. seriesOptions[AXIS] === axisOptions.id) ||
  29604. (typeof seriesOptions[AXIS] ===
  29605. 'undefined' &&
  29606. axisOptions.index === 0)) {
  29607. // register this series in the axis.series lookup
  29608. series.insert(axis.series);
  29609. // set this series.xAxis or series.yAxis reference
  29610. /**
  29611. * Read only. The unique xAxis object associated
  29612. * with the series.
  29613. *
  29614. * @name Highcharts.Series#xAxis
  29615. * @type {Highcharts.Axis}
  29616. */
  29617. /**
  29618. * Read only. The unique yAxis object associated
  29619. * with the series.
  29620. *
  29621. * @name Highcharts.Series#yAxis
  29622. * @type {Highcharts.Axis}
  29623. */
  29624. series[AXIS] = axis;
  29625. // mark dirty for redraw
  29626. axis.isDirty = true;
  29627. }
  29628. });
  29629. // The series needs an X and an Y axis
  29630. if (!series[AXIS] &&
  29631. series.optionalAxis !== AXIS) {
  29632. H.error(18, true, chart);
  29633. }
  29634. });
  29635. });
  29636. },
  29637. /**
  29638. * For simple series types like line and column, the data values are
  29639. * held in arrays like xData and yData for quick lookup to find extremes
  29640. * and more. For multidimensional series like bubble and map, this can
  29641. * be extended with arrays like zData and valueData by adding to the
  29642. * `series.parallelArrays` array.
  29643. *
  29644. * @private
  29645. * @function Highcharts.Series#updateParallelArrays
  29646. * @param {Highcharts.Point} point
  29647. * @param {number|string} i
  29648. * @return {void}
  29649. */
  29650. updateParallelArrays: function (point, i) {
  29651. var series = point.series, args = arguments, fn = isNumber(i) ?
  29652. // Insert the value in the given position
  29653. function (key) {
  29654. var val = key === 'y' && series.toYData ?
  29655. series.toYData(point) :
  29656. point[key];
  29657. series[key + 'Data'][i] = val;
  29658. } :
  29659. // Apply the method specified in i with the following
  29660. // arguments as arguments
  29661. function (key) {
  29662. Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
  29663. };
  29664. series.parallelArrays.forEach(fn);
  29665. },
  29666. /**
  29667. * Define hasData functions for series. These return true if there
  29668. * are data points on this series within the plot area.
  29669. *
  29670. * @private
  29671. * @function Highcharts.Series#hasData
  29672. * @return {boolean}
  29673. */
  29674. hasData: function () {
  29675. return ((this.visible &&
  29676. typeof this.dataMax !== 'undefined' &&
  29677. typeof this.dataMin !== 'undefined') || ( // #3703
  29678. this.visible &&
  29679. this.yData &&
  29680. this.yData.length > 0) // #9758
  29681. );
  29682. },
  29683. /**
  29684. * Return an auto incremented x value based on the pointStart and
  29685. * pointInterval options. This is only used if an x value is not given
  29686. * for the point that calls autoIncrement.
  29687. *
  29688. * @private
  29689. * @function Highcharts.Series#autoIncrement
  29690. * @return {number}
  29691. */
  29692. autoIncrement: function () {
  29693. var options = this.options, xIncrement = this.xIncrement, date, pointInterval, pointIntervalUnit = options.pointIntervalUnit, time = this.chart.time;
  29694. xIncrement = pick(xIncrement, options.pointStart, 0);
  29695. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  29696. // Added code for pointInterval strings
  29697. if (pointIntervalUnit) {
  29698. date = new time.Date(xIncrement);
  29699. if (pointIntervalUnit === 'day') {
  29700. time.set('Date', date, time.get('Date', date) + pointInterval);
  29701. }
  29702. else if (pointIntervalUnit === 'month') {
  29703. time.set('Month', date, time.get('Month', date) + pointInterval);
  29704. }
  29705. else if (pointIntervalUnit === 'year') {
  29706. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  29707. }
  29708. pointInterval = date.getTime() - xIncrement;
  29709. }
  29710. this.xIncrement = xIncrement + pointInterval;
  29711. return xIncrement;
  29712. },
  29713. /**
  29714. * Internal function to set properties for series if data sorting is
  29715. * enabled.
  29716. *
  29717. * @private
  29718. * @function Highcharts.Series#setDataSortingOptions
  29719. * @return {void}
  29720. */
  29721. setDataSortingOptions: function () {
  29722. var options = this.options;
  29723. extend(this, {
  29724. requireSorting: false,
  29725. sorted: false,
  29726. enabledDataSorting: true,
  29727. allowDG: false
  29728. });
  29729. // To allow unsorted data for column series.
  29730. if (!defined(options.pointRange)) {
  29731. options.pointRange = 1;
  29732. }
  29733. },
  29734. /**
  29735. * Set the series options by merging from the options tree. Called
  29736. * internally on initializing and updating series. This function will
  29737. * not redraw the series. For API usage, use {@link Series#update}.
  29738. * @private
  29739. * @function Highcharts.Series#setOptions
  29740. * @param {Highcharts.SeriesOptionsType} itemOptions
  29741. * The series options.
  29742. * @return {Highcharts.SeriesOptionsType}
  29743. * @fires Highcharts.Series#event:afterSetOptions
  29744. */
  29745. setOptions: function (itemOptions) {
  29746. var chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), options, zones, zone, styledMode = chart.styledMode, e = {
  29747. plotOptions: plotOptions,
  29748. userOptions: seriesUserOptions
  29749. };
  29750. fireEvent(this, 'setOptions', e);
  29751. // These may be modified by the event
  29752. var typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {});
  29753. // use copy to prevent undetected changes (#9762)
  29754. this.userOptions = e.userOptions;
  29755. options = merge(typeOptions, plotOptions.series,
  29756. // #3881, chart instance plotOptions[type] should trump
  29757. // plotOptions.series
  29758. userOptions.plotOptions &&
  29759. userOptions.plotOptions[this.type], seriesUserOptions);
  29760. // The tooltip options are merged between global and series specific
  29761. // options. Importance order asscendingly:
  29762. // globals: (1)tooltip, (2)plotOptions.series,
  29763. // (3)plotOptions[this.type]
  29764. // init userOptions with possible later updates: 4-6 like 1-3 and
  29765. // (7)this series options
  29766. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  29767. defaultOptions.plotOptions.series &&
  29768. defaultOptions.plotOptions.series.tooltip, // 2
  29769. defaultOptions.plotOptions[this.type].tooltip, // 3
  29770. chartOptions.tooltip.userOptions, // 4
  29771. plotOptions.series &&
  29772. plotOptions.series.tooltip, // 5
  29773. plotOptions[this.type].tooltip, // 6
  29774. seriesUserOptions.tooltip // 7
  29775. );
  29776. // When shared tooltip, stickyTracking is true by default,
  29777. // unless user says otherwise.
  29778. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptions[this.type] &&
  29779. userPlotOptions[this.type].stickyTracking, userPlotOptions.series && userPlotOptions.series.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  29780. true :
  29781. options.stickyTracking));
  29782. // Delete marker object if not allowed (#1125)
  29783. if (typeOptions.marker === null) {
  29784. delete options.marker;
  29785. }
  29786. // Handle color zones
  29787. this.zoneAxis = options.zoneAxis;
  29788. zones = this.zones = (options.zones || []).slice();
  29789. if ((options.negativeColor || options.negativeFillColor) &&
  29790. !options.zones) {
  29791. zone = {
  29792. value: options[this.zoneAxis + 'Threshold'] ||
  29793. options.threshold ||
  29794. 0,
  29795. className: 'highcharts-negative'
  29796. };
  29797. if (!styledMode) {
  29798. zone.color = options.negativeColor;
  29799. zone.fillColor = options.negativeFillColor;
  29800. }
  29801. zones.push(zone);
  29802. }
  29803. if (zones.length) { // Push one extra zone for the rest
  29804. if (defined(zones[zones.length - 1].value)) {
  29805. zones.push(styledMode ? {} : {
  29806. color: this.color,
  29807. fillColor: this.fillColor
  29808. });
  29809. }
  29810. }
  29811. fireEvent(this, 'afterSetOptions', { options: options });
  29812. return options;
  29813. },
  29814. /**
  29815. * Return series name in "Series {Number}" format or the one defined by
  29816. * a user. This method can be simply overridden as series name format
  29817. * can vary (e.g. technical indicators).
  29818. *
  29819. * @function Highcharts.Series#getName
  29820. * @return {string}
  29821. * The series name.
  29822. */
  29823. getName: function () {
  29824. // #4119
  29825. return pick(this.options.name, 'Series ' + (this.index + 1));
  29826. },
  29827. /**
  29828. * @private
  29829. * @function Highcharts.Series#getCyclic
  29830. * @param {string} prop
  29831. * @param {*} [value]
  29832. * @param {Highcharts.Dictionary<any>} [defaults]
  29833. * @return {void}
  29834. */
  29835. getCyclic: function (prop, value, defaults) {
  29836. 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;
  29837. if (!value) {
  29838. // Pick up either the colorIndex option, or the _colorIndex
  29839. // after Series.update()
  29840. setting = pick(userOptions[indexName], userOptions['_' + indexName]);
  29841. if (defined(setting)) { // after Series.update()
  29842. i = setting;
  29843. }
  29844. else {
  29845. // #6138
  29846. if (!chart.series.length) {
  29847. chart[counterName] = 0;
  29848. }
  29849. userOptions['_' + indexName] = i =
  29850. chart[counterName] % len;
  29851. chart[counterName] += 1;
  29852. }
  29853. if (defaults) {
  29854. value = defaults[i];
  29855. }
  29856. }
  29857. // Set the colorIndex
  29858. if (typeof i !== 'undefined') {
  29859. this[indexName] = i;
  29860. }
  29861. this[prop] = value;
  29862. },
  29863. /**
  29864. * Get the series' color based on either the options or pulled from
  29865. * global options.
  29866. *
  29867. * @private
  29868. * @function Highcharts.Series#getColor
  29869. * @return {void}
  29870. */
  29871. getColor: function () {
  29872. if (this.chart.styledMode) {
  29873. this.getCyclic('color');
  29874. }
  29875. else if (this.options.colorByPoint) {
  29876. // #4359, selected slice got series.color even when colorByPoint
  29877. // was set.
  29878. this.options.color = null;
  29879. }
  29880. else {
  29881. this.getCyclic('color', this.options.color ||
  29882. defaultPlotOptions[this.type].color, this.chart.options.colors);
  29883. }
  29884. },
  29885. /**
  29886. * Get the series' symbol based on either the options or pulled from
  29887. * global options.
  29888. *
  29889. * @private
  29890. * @function Highcharts.Series#getSymbol
  29891. * @return {void}
  29892. */
  29893. getSymbol: function () {
  29894. var seriesMarkerOption = this.options.marker;
  29895. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  29896. },
  29897. /**
  29898. * Finds the index of an existing point that matches the given point
  29899. * options.
  29900. *
  29901. * @private
  29902. * @function Highcharts.Series#findPointIndex
  29903. * @param {Highcharts.PointOptionsObject} optionsObject
  29904. * The options of the point.
  29905. * @param {number} fromIndex
  29906. * The index to start searching from, used for optimizing
  29907. * series with required sorting.
  29908. * @returns {number|undefined}
  29909. * Returns the index of a matching point, or undefined if no
  29910. * match is found.
  29911. */
  29912. findPointIndex: function (optionsObject, fromIndex) {
  29913. var id = optionsObject.id, x = optionsObject.x, oldData = this.points, matchingPoint, matchedById, pointIndex, matchKey, dataSorting = this.options.dataSorting;
  29914. if (id) {
  29915. matchingPoint = this.chart.get(id);
  29916. }
  29917. else if (this.linkedParent || this.enabledDataSorting) {
  29918. matchKey = (dataSorting && dataSorting.matchByName) ?
  29919. 'name' : 'index';
  29920. matchingPoint = H.find(oldData, function (oldPoint) {
  29921. return !oldPoint.touched && oldPoint[matchKey] ===
  29922. optionsObject[matchKey];
  29923. });
  29924. // Add unmatched point as a new point
  29925. if (!matchingPoint) {
  29926. return void 0;
  29927. }
  29928. }
  29929. if (matchingPoint) {
  29930. pointIndex = matchingPoint && matchingPoint.index;
  29931. if (typeof pointIndex !== 'undefined') {
  29932. matchedById = true;
  29933. }
  29934. }
  29935. // Search for the same X in the existing data set
  29936. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  29937. pointIndex = this.xData.indexOf(x, fromIndex);
  29938. }
  29939. // Reduce pointIndex if data is cropped
  29940. if (pointIndex !== -1 &&
  29941. typeof pointIndex !== 'undefined' &&
  29942. this.cropped) {
  29943. pointIndex = (pointIndex >= this.cropStart) ?
  29944. pointIndex - this.cropStart : pointIndex;
  29945. }
  29946. if (!matchedById &&
  29947. oldData[pointIndex] && oldData[pointIndex].touched) {
  29948. pointIndex = void 0;
  29949. }
  29950. return pointIndex;
  29951. },
  29952. /**
  29953. * @private
  29954. * @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
  29955. */
  29956. drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
  29957. /**
  29958. * Internal function called from setData. If the point count is the same
  29959. * as is was, or if there are overlapping X values, just run
  29960. * Point.update which is cheaper, allows animation, and keeps references
  29961. * to points. This also allows adding or removing points if the X-es
  29962. * don't match.
  29963. *
  29964. * @private
  29965. * @function Highcharts.Series#updateData
  29966. *
  29967. * @param {Array<Highcharts.PointOptionsType>} data
  29968. *
  29969. * @return {boolean}
  29970. */
  29971. updateData: function (data, animation) {
  29972. 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;
  29973. this.xIncrement = null;
  29974. // Iterate the new data
  29975. data.forEach(function (pointOptions, i) {
  29976. var id, x, pointIndex, optionsObject = (defined(pointOptions) &&
  29977. this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
  29978. // Get the x of the new data point
  29979. x = optionsObject.x;
  29980. id = optionsObject.id;
  29981. if (id || isNumber(x)) {
  29982. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  29983. // Matching X not found
  29984. // or used already due to ununique x values (#8995),
  29985. // add point (but later)
  29986. if (pointIndex === -1 ||
  29987. typeof pointIndex === 'undefined') {
  29988. pointsToAdd.push(pointOptions);
  29989. // Matching X found, update
  29990. }
  29991. else if (oldData[pointIndex] &&
  29992. pointOptions !== options.data[pointIndex]) {
  29993. oldData[pointIndex].update(pointOptions, false, null, false);
  29994. // Mark it touched, below we will remove all points that
  29995. // are not touched.
  29996. oldData[pointIndex].touched = true;
  29997. // Speed optimize by only searching after last known
  29998. // index. Performs ~20% bettor on large data sets.
  29999. if (requireSorting) {
  30000. lastIndex = pointIndex + 1;
  30001. }
  30002. // Point exists, no changes, don't remove it
  30003. }
  30004. else if (oldData[pointIndex]) {
  30005. oldData[pointIndex].touched = true;
  30006. }
  30007. // If the length is equal and some of the nodes had a
  30008. // match in the same position, we don't want to remove
  30009. // non-matches.
  30010. if (!equalLength ||
  30011. i !== pointIndex ||
  30012. (dataSorting && dataSorting.enabled) ||
  30013. this.hasDerivedData) {
  30014. hasUpdatedByKey = true;
  30015. }
  30016. }
  30017. else {
  30018. // Gather all points that are not matched
  30019. pointsToAdd.push(pointOptions);
  30020. }
  30021. }, this);
  30022. // Remove points that don't exist in the updated data set
  30023. if (hasUpdatedByKey) {
  30024. i = oldData.length;
  30025. while (i--) {
  30026. point = oldData[i];
  30027. if (point && !point.touched && point.remove) {
  30028. point.remove(false, animation);
  30029. }
  30030. }
  30031. // If we did not find keys (ids or x-values), and the length is the
  30032. // same, update one-to-one
  30033. }
  30034. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  30035. data.forEach(function (point, i) {
  30036. // .update doesn't exist on a linked, hidden series (#3709)
  30037. // (#10187)
  30038. if (oldData[i].update && point !== oldData[i].y) {
  30039. oldData[i].update(point, false, null, false);
  30040. }
  30041. });
  30042. // Don't add new points since those configs are used above
  30043. pointsToAdd.length = 0;
  30044. // Did not succeed in updating data
  30045. }
  30046. else {
  30047. succeeded = false;
  30048. }
  30049. oldData.forEach(function (point) {
  30050. if (point) {
  30051. point.touched = false;
  30052. }
  30053. });
  30054. if (!succeeded) {
  30055. return false;
  30056. }
  30057. // Add new points
  30058. pointsToAdd.forEach(function (point) {
  30059. this.addPoint(point, false, null, null, false);
  30060. }, this);
  30061. if (this.xIncrement === null &&
  30062. this.xData &&
  30063. this.xData.length) {
  30064. this.xIncrement = arrayMax(this.xData);
  30065. this.autoIncrement();
  30066. }
  30067. return true;
  30068. },
  30069. /**
  30070. * Apply a new set of data to the series and optionally redraw it. The
  30071. * new data array is passed by reference (except in case of
  30072. * `updatePoints`), and may later be mutated when updating the chart
  30073. * data.
  30074. *
  30075. * Note the difference in behaviour when setting the same amount of
  30076. * points, or a different amount of points, as handled by the
  30077. * `updatePoints` parameter.
  30078. *
  30079. * @sample highcharts/members/series-setdata/
  30080. * Set new data from a button
  30081. * @sample highcharts/members/series-setdata-pie/
  30082. * Set data in a pie
  30083. * @sample stock/members/series-setdata/
  30084. * Set new data in Highstock
  30085. * @sample maps/members/series-setdata/
  30086. * Set new data in Highmaps
  30087. *
  30088. * @function Highcharts.Series#setData
  30089. *
  30090. * @param {Array<Highcharts.PointOptionsType>} data
  30091. * Takes an array of data in the same format as described under
  30092. * `series.{type}.data` for the given series type, for example a
  30093. * line series would take data in the form described under
  30094. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  30095. *
  30096. * @param {boolean} [redraw=true]
  30097. * Whether to redraw the chart after the series is altered. If
  30098. * doing more operations on the chart, it is a good idea to set
  30099. * redraw to false and call {@link Chart#redraw} after.
  30100. *
  30101. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  30102. * When the updated data is the same length as the existing data,
  30103. * points will be updated by default, and animation visualizes
  30104. * how the points are changed. Set false to disable animation, or
  30105. * a configuration object to set duration or easing.
  30106. *
  30107. * @param {boolean} [updatePoints=true]
  30108. * When this is true, points will be updated instead of replaced
  30109. * whenever possible. This occurs a) when the updated data is the
  30110. * same length as the existing data, b) when points are matched
  30111. * by their id's, or c) when points can be matched by X values.
  30112. * This allows updating with animation and performs better. In
  30113. * this case, the original array is not passed by reference. Set
  30114. * `false` to prevent.
  30115. *
  30116. * @return {void}
  30117. */
  30118. setData: function (data, redraw, animation, updatePoints) {
  30119. 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;
  30120. data = data || [];
  30121. dataLength = data.length;
  30122. redraw = pick(redraw, true);
  30123. if (dataSorting && dataSorting.enabled) {
  30124. data = this.sortData(data);
  30125. }
  30126. // First try to run Point.update which is cheaper, allows animation,
  30127. // and keeps references to points.
  30128. if (updatePoints !== false &&
  30129. dataLength &&
  30130. oldDataLength &&
  30131. !series.cropped &&
  30132. !series.hasGroupedData &&
  30133. series.visible &&
  30134. // Soft updating has no benefit in boost, and causes JS error
  30135. // (#8355)
  30136. !series.isSeriesBoosting) {
  30137. updatedData = this.updateData(data, animation);
  30138. }
  30139. if (!updatedData) {
  30140. // Reset properties
  30141. series.xIncrement = null;
  30142. series.colorCounter = 0; // for series with colorByPoint (#1547)
  30143. // Update parallel arrays
  30144. this.parallelArrays.forEach(function (key) {
  30145. series[key + 'Data'].length = 0;
  30146. });
  30147. // In turbo mode, only one- or twodimensional arrays of numbers
  30148. // are allowed. The first value is tested, and we assume that
  30149. // all the rest are defined the same way. Although the 'for'
  30150. // loops are similar, they are repeated inside each if-else
  30151. // conditional for max performance.
  30152. if (turboThreshold && dataLength > turboThreshold) {
  30153. firstPoint = series.getFirstValidPoint(data);
  30154. if (isNumber(firstPoint)) { // assume all points are numbers
  30155. for (i = 0; i < dataLength; i++) {
  30156. xData[i] = this.autoIncrement();
  30157. yData[i] = data[i];
  30158. }
  30159. // Assume all points are arrays when first point is
  30160. }
  30161. else if (isArray(firstPoint)) {
  30162. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  30163. for (i = 0; i < dataLength; i++) {
  30164. pt = data[i];
  30165. xData[i] = pt[0];
  30166. yData[i] =
  30167. pt.slice(1, valueCount + 1);
  30168. }
  30169. }
  30170. else { // [x, y]
  30171. if (keys) {
  30172. indexOfX = keys.indexOf('x');
  30173. indexOfY = keys.indexOf('y');
  30174. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  30175. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  30176. }
  30177. for (i = 0; i < dataLength; i++) {
  30178. pt = data[i];
  30179. xData[i] = pt[indexOfX];
  30180. yData[i] = pt[indexOfY];
  30181. }
  30182. }
  30183. }
  30184. else {
  30185. // Highcharts expects configs to be numbers or arrays in
  30186. // turbo mode
  30187. H.error(12, false, chart);
  30188. }
  30189. }
  30190. else {
  30191. for (i = 0; i < dataLength; i++) {
  30192. // stray commas in oldIE:
  30193. if (typeof data[i] !== 'undefined') {
  30194. pt = { series: series };
  30195. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  30196. series.updateParallelArrays(pt, i);
  30197. }
  30198. }
  30199. }
  30200. // Forgetting to cast strings to numbers is a common caveat when
  30201. // handling CSV or JSON
  30202. if (yData && isString(yData[0])) {
  30203. H.error(14, true, chart);
  30204. }
  30205. series.data = [];
  30206. series.options.data = series.userOptions.data = data;
  30207. // destroy old points
  30208. i = oldDataLength;
  30209. while (i--) {
  30210. if (oldData[i] && oldData[i].destroy) {
  30211. oldData[i].destroy();
  30212. }
  30213. }
  30214. // reset minRange (#878)
  30215. if (xAxis) {
  30216. xAxis.minRange = xAxis.userMinRange;
  30217. }
  30218. // redraw
  30219. series.isDirty = chart.isDirtyBox = true;
  30220. series.isDirtyData = !!oldData;
  30221. animation = false;
  30222. }
  30223. // Typically for pie series, points need to be processed and
  30224. // generated prior to rendering the legend
  30225. if (options.legendType === 'point') {
  30226. this.processData();
  30227. this.generatePoints();
  30228. }
  30229. if (redraw) {
  30230. chart.redraw(animation);
  30231. }
  30232. },
  30233. /**
  30234. * Internal function to sort series data
  30235. *
  30236. * @private
  30237. * @function Highcharts.Series#sortData
  30238. * @param {Array<Highcharts.PointOptionsType>} data
  30239. * Force data grouping.
  30240. * @return {Array<Highcharts.PointOptionsObject>}
  30241. */
  30242. sortData: function (data) {
  30243. var series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', sortedData, getPointOptionsObject = function (series, pointOptions) {
  30244. return (defined(pointOptions) &&
  30245. series.pointClass.prototype.optionsToObject.call({
  30246. series: series
  30247. }, pointOptions)) || {};
  30248. };
  30249. data.forEach(function (pointOptions, i) {
  30250. data[i] = getPointOptionsObject(series, pointOptions);
  30251. data[i].index = i;
  30252. }, this);
  30253. // Sorting
  30254. sortedData = data.concat().sort(function (a, b) {
  30255. return isNumber(b[sortKey]) ?
  30256. b[sortKey] - a[sortKey] :
  30257. -1;
  30258. });
  30259. // Set x value depending on the position in the array
  30260. sortedData.forEach(function (point, i) {
  30261. point.x = i;
  30262. }, this);
  30263. // Set the same x for linked series points if they don't have their
  30264. // own sorting
  30265. if (series.linkedSeries) {
  30266. series.linkedSeries.forEach(function (linkedSeries) {
  30267. var options = linkedSeries.options, seriesData = options.data;
  30268. if ((!options.dataSorting ||
  30269. !options.dataSorting.enabled) &&
  30270. seriesData) {
  30271. seriesData.forEach(function (pointOptions, i) {
  30272. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  30273. if (data[i]) {
  30274. seriesData[i].x = data[i].x;
  30275. seriesData[i].index = i;
  30276. }
  30277. });
  30278. linkedSeries.setData(seriesData, false);
  30279. }
  30280. });
  30281. }
  30282. return data;
  30283. },
  30284. /**
  30285. * Internal function to process the data by cropping away unused data
  30286. * points if the series is longer than the crop threshold. This saves
  30287. * computing time for large series. In Highstock, this function is
  30288. * extended to provide data grouping.
  30289. *
  30290. * @private
  30291. * @function Highcharts.Series#processData
  30292. * @param {boolean} [force]
  30293. * Force data grouping.
  30294. * @return {boolean|undefined}
  30295. */
  30296. processData: function (force) {
  30297. var series = this,
  30298. // copied during slice operation:
  30299. processedXData = series.xData, processedYData = series.yData, dataLength = processedXData.length, croppedData, cropStart = 0, cropped, distance, closestPointRange, xAxis = series.xAxis, i, // loop variable
  30300. options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = series.getExtremesFromAll ||
  30301. options.getExtremesFromAll, // #4599
  30302. isCartesian = series.isCartesian, xExtremes, val2lin = xAxis && xAxis.val2lin, isLog = xAxis && xAxis.isLog, throwOnUnsorted = series.requireSorting, min, max;
  30303. // If the series data or axes haven't changed, don't go through
  30304. // this. Return false to pass the message on to override methods
  30305. // like in data grouping.
  30306. if (isCartesian &&
  30307. !series.isDirty &&
  30308. !xAxis.isDirty &&
  30309. !series.yAxis.isDirty &&
  30310. !force) {
  30311. return false;
  30312. }
  30313. if (xAxis) {
  30314. // corrected for log axis (#3053)
  30315. xExtremes = xAxis.getExtremes();
  30316. min = xExtremes.min;
  30317. max = xExtremes.max;
  30318. }
  30319. // optionally filter out points outside the plot area
  30320. if (isCartesian &&
  30321. series.sorted &&
  30322. !getExtremesFromAll &&
  30323. (!cropThreshold ||
  30324. dataLength > cropThreshold ||
  30325. series.forceCrop)) {
  30326. // it's outside current extremes
  30327. if (processedXData[dataLength - 1] < min ||
  30328. processedXData[0] > max) {
  30329. processedXData = [];
  30330. processedYData = [];
  30331. // only crop if it's actually spilling out
  30332. }
  30333. else if (series.yData && (processedXData[0] < min ||
  30334. processedXData[dataLength - 1] > max)) {
  30335. croppedData = this.cropData(series.xData, series.yData, min, max);
  30336. processedXData = croppedData.xData;
  30337. processedYData = croppedData.yData;
  30338. cropStart = croppedData.start;
  30339. cropped = true;
  30340. }
  30341. }
  30342. // Find the closest distance between processed points
  30343. i = processedXData.length || 1;
  30344. while (--i) {
  30345. distance = (isLog ?
  30346. (val2lin(processedXData[i]) -
  30347. val2lin(processedXData[i - 1])) :
  30348. (processedXData[i] -
  30349. processedXData[i - 1]));
  30350. if (distance > 0 &&
  30351. (typeof closestPointRange === 'undefined' ||
  30352. distance < closestPointRange)) {
  30353. closestPointRange = distance;
  30354. // Unsorted data is not supported by the line tooltip, as well
  30355. // as data grouping and navigation in Stock charts (#725) and
  30356. // width calculation of columns (#1900)
  30357. }
  30358. else if (distance < 0 && throwOnUnsorted) {
  30359. H.error(15, false, series.chart);
  30360. throwOnUnsorted = false; // Only once
  30361. }
  30362. }
  30363. // Record the properties
  30364. series.cropped = cropped; // undefined or true
  30365. series.cropStart = cropStart;
  30366. series.processedXData = processedXData;
  30367. series.processedYData = processedYData;
  30368. series.closestPointRange =
  30369. series.basePointRange = closestPointRange;
  30370. },
  30371. /**
  30372. * Iterate over xData and crop values between min and max. Returns
  30373. * object containing crop start/end cropped xData with corresponding
  30374. * part of yData, dataMin and dataMax within the cropped range.
  30375. *
  30376. * @private
  30377. * @function Highcharts.Series#cropData
  30378. * @param {Array<number>} xData
  30379. * @param {Array<number>} yData
  30380. * @param {number} min
  30381. * @param {number} max
  30382. * @param {number} [cropShoulder]
  30383. * @return {Highcharts.SeriesCropDataObject}
  30384. */
  30385. cropData: function (xData, yData, min, max, cropShoulder) {
  30386. var dataLength = xData.length, cropStart = 0, cropEnd = dataLength, i, j;
  30387. // line-type series need one point outside
  30388. cropShoulder = pick(cropShoulder, this.cropShoulder);
  30389. // iterate up to find slice start
  30390. for (i = 0; i < dataLength; i++) {
  30391. if (xData[i] >= min) {
  30392. cropStart = Math.max(0, i - cropShoulder);
  30393. break;
  30394. }
  30395. }
  30396. // proceed to find slice end
  30397. for (j = i; j < dataLength; j++) {
  30398. if (xData[j] > max) {
  30399. cropEnd = j + cropShoulder;
  30400. break;
  30401. }
  30402. }
  30403. return {
  30404. xData: xData.slice(cropStart, cropEnd),
  30405. yData: yData.slice(cropStart, cropEnd),
  30406. start: cropStart,
  30407. end: cropEnd
  30408. };
  30409. },
  30410. /**
  30411. * Generate the data point after the data has been processed by cropping
  30412. * away unused points and optionally grouped in Highcharts Stock.
  30413. *
  30414. * @private
  30415. * @function Highcharts.Series#generatePoints
  30416. * @return {void}
  30417. */
  30418. generatePoints: function () {
  30419. 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;
  30420. if (!data && !hasGroupedData) {
  30421. var arr = [];
  30422. arr.length = dataOptions.length;
  30423. data = series.data = arr;
  30424. }
  30425. if (keys && hasGroupedData) {
  30426. // grouped data has already applied keys (#6590)
  30427. series.options.keys = false;
  30428. }
  30429. for (i = 0; i < processedDataLength; i++) {
  30430. cursor = cropStart + i;
  30431. if (!hasGroupedData) {
  30432. point = data[cursor];
  30433. // #970:
  30434. if (!point &&
  30435. typeof dataOptions[cursor] !== 'undefined') {
  30436. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  30437. }
  30438. }
  30439. else {
  30440. // splat the y data in case of ohlc data array
  30441. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  30442. /**
  30443. * Highstock only. If a point object is created by data
  30444. * grouping, it doesn't reflect actual points in the raw
  30445. * data. In this case, the `dataGroup` property holds
  30446. * information that points back to the raw data.
  30447. *
  30448. * - `dataGroup.start` is the index of the first raw data
  30449. * point in the group.
  30450. *
  30451. * - `dataGroup.length` is the amount of points in the
  30452. * group.
  30453. *
  30454. * @product highstock
  30455. *
  30456. * @name Highcharts.Point#dataGroup
  30457. * @type {Highcharts.DataGroupingInfoObject|undefined}
  30458. */
  30459. point.dataGroup = series.groupMap[i];
  30460. if (point.dataGroup.options) {
  30461. point.options = point.dataGroup.options;
  30462. extend(point, point.dataGroup.options);
  30463. // Collision of props and options (#9770)
  30464. delete point.dataLabels;
  30465. }
  30466. }
  30467. if (point) { // #6279
  30468. /**
  30469. * Contains the point's index in the `Series.points` array.
  30470. *
  30471. * @name Highcharts.Point#index
  30472. * @type {number}
  30473. * @readonly
  30474. */
  30475. point.index = cursor; // For faster access in Point.update
  30476. points[i] = point;
  30477. }
  30478. }
  30479. // restore keys options (#6590)
  30480. series.options.keys = keys;
  30481. // Hide cropped-away points - this only runs when the number of
  30482. // points is above cropThreshold, or when swithching view from
  30483. // non-grouped data to grouped data (#637)
  30484. if (data &&
  30485. (processedDataLength !== (dataLength = data.length) ||
  30486. hasGroupedData)) {
  30487. for (i = 0; i < dataLength; i++) {
  30488. // when has grouped data, clear all points
  30489. if (i === cropStart && !hasGroupedData) {
  30490. i += processedDataLength;
  30491. }
  30492. if (data[i]) {
  30493. data[i].destroyElements();
  30494. data[i].plotX = void 0; // #1003
  30495. }
  30496. }
  30497. }
  30498. /**
  30499. * Read only. An array containing those values converted to points.
  30500. * In case the series data length exceeds the `cropThreshold`, or if
  30501. * the data is grouped, `series.data` doesn't contain all the
  30502. * points. Also, in case a series is hidden, the `data` array may be
  30503. * empty. To access raw values, `series.options.data` will always be
  30504. * up to date. `Series.data` only contains the points that have been
  30505. * created on demand. To modify the data, use
  30506. * {@link Highcharts.Series#setData} or
  30507. * {@link Highcharts.Point#update}.
  30508. *
  30509. * @see Series.points
  30510. *
  30511. * @name Highcharts.Series#data
  30512. * @type {Array<Highcharts.Point>}
  30513. */
  30514. series.data = data;
  30515. /**
  30516. * An array containing all currently visible point objects. In case
  30517. * of cropping, the cropped-away points are not part of this array.
  30518. * The `series.points` array starts at `series.cropStart` compared
  30519. * to `series.data` and `series.options.data`. If however the series
  30520. * data is grouped, these can't be correlated one to one. To modify
  30521. * the data, use {@link Highcharts.Series#setData} or
  30522. * {@link Highcharts.Point#update}.
  30523. *
  30524. * @name Highcharts.Series#points
  30525. * @type {Array<Highcharts.Point>}
  30526. */
  30527. series.points = points;
  30528. fireEvent(this, 'afterGeneratePoints');
  30529. },
  30530. /**
  30531. * Get current X extremes for the visible data.
  30532. *
  30533. * @private
  30534. * @function Highcharts.Series#getXExtremes
  30535. *
  30536. * @param {Array<number>} xData
  30537. * The data to inspect. Defaults to the current data within the
  30538. * visible range.
  30539. * @return {Highcharts.RangeObject}
  30540. */
  30541. getXExtremes: function (xData) {
  30542. return {
  30543. min: arrayMin(xData),
  30544. max: arrayMax(xData)
  30545. };
  30546. },
  30547. /**
  30548. * Calculate Y extremes for the visible data. The result is set as
  30549. * `dataMin` and `dataMax` on the Series item.
  30550. *
  30551. * @private
  30552. * @function Highcharts.Series#getExtremes
  30553. * @param {Array<number>} [yData]
  30554. * The data to inspect. Defaults to the current data within the
  30555. * visible range.
  30556. * @return {void}
  30557. */
  30558. getExtremes: function (yData) {
  30559. var xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, yDataLength, activeYData = [], activeCounter = 0,
  30560. // #2117, need to compensate for log X axis
  30561. xExtremes, xMin = 0, xMax = 0, validValue, withinRange,
  30562. // Handle X outside the viewed area. This does not work with
  30563. // non-sorted data like scatter (#7639).
  30564. shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false, x, y, i, j;
  30565. yData = yData || this.stackedYData || this.processedYData || [];
  30566. yDataLength = yData.length;
  30567. if (xAxis) {
  30568. xExtremes = xAxis.getExtremes();
  30569. xMin = xExtremes.min;
  30570. xMax = xExtremes.max;
  30571. }
  30572. for (i = 0; i < yDataLength; i++) {
  30573. x = xData[i];
  30574. y = yData[i];
  30575. // For points within the visible range, including the first
  30576. // point outside the visible range (#7061), consider y extremes.
  30577. validValue = ((isNumber(y) || isArray(y)) &&
  30578. ((y.length || y > 0) || !positiveValuesOnly));
  30579. withinRange = (this.getExtremesFromAll ||
  30580. this.options.getExtremesFromAll ||
  30581. this.cropped ||
  30582. !xAxis || // for colorAxis support
  30583. ((xData[i + shoulder] || x) >= xMin &&
  30584. (xData[i - shoulder] || x) <= xMax));
  30585. if (validValue && withinRange) {
  30586. j = y.length;
  30587. if (j) { // array, like ohlc or range data
  30588. while (j--) {
  30589. if (isNumber(y[j])) { // #7380, #11513
  30590. activeYData[activeCounter++] = y[j];
  30591. }
  30592. }
  30593. }
  30594. else {
  30595. activeYData[activeCounter++] = y;
  30596. }
  30597. }
  30598. }
  30599. /**
  30600. * Contains the minimum value of the series' data point.
  30601. * @name Highcharts.Series#dataMin
  30602. * @type {number}
  30603. * @readonly
  30604. */
  30605. this.dataMin = arrayMin(activeYData);
  30606. /**
  30607. * Contains the maximum value of the series' data point.
  30608. * @name Highcharts.Series#dataMax
  30609. * @type {number}
  30610. * @readonly
  30611. */
  30612. this.dataMax = arrayMax(activeYData);
  30613. fireEvent(this, 'afterGetExtremes');
  30614. },
  30615. /**
  30616. * Find and return the first non null point in the data
  30617. *
  30618. * @private
  30619. * @function Highcharts.Series.getFirstValidPoint
  30620. * @param {Array<Highcharts.PointOptionsType>} data
  30621. * Array of options for points
  30622. *
  30623. * @return {Highcharts.PointOptionsType}
  30624. */
  30625. getFirstValidPoint: function (data) {
  30626. var firstPoint = null, dataLength = data.length, i = 0;
  30627. while (firstPoint === null && i < dataLength) {
  30628. firstPoint = data[i];
  30629. i++;
  30630. }
  30631. return firstPoint;
  30632. },
  30633. /**
  30634. * Translate data points from raw data values to chart specific
  30635. * positioning data needed later in the `drawPoints` and `drawGraph`
  30636. * functions. This function can be overridden in plugins and custom
  30637. * series type implementations.
  30638. *
  30639. * @function Highcharts.Series#translate
  30640. * @return {void}
  30641. * @fires Highcharts.Series#events:translate
  30642. */
  30643. translate: function () {
  30644. if (!this.processedXData) { // hidden series
  30645. this.processData();
  30646. }
  30647. this.generatePoints();
  30648. 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
  30649. dynamicallyPlaced = isNumber(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0, plotX, plotY, lastPlotX, stackIndicator, zoneAxis = this.zoneAxis || 'y', closestPointRangePx = Number.MAX_VALUE;
  30650. /**
  30651. * Plotted coordinates need to be within a limited range. Drawing
  30652. * too far outside the viewport causes various rendering issues
  30653. * (#3201, #3923, #7555).
  30654. * @private
  30655. */
  30656. function limitedRange(val) {
  30657. return clamp(val, -1e5, 1e5);
  30658. }
  30659. // Translate each point
  30660. for (i = 0; i < dataLength; i++) {
  30661. var point = points[i], xValue = point.x, yValue = point.y, yBottom = point.low, stack = stacking && yAxis.stacks[(series.negStacks &&
  30662. yValue <
  30663. (stackThreshold ? 0 : threshold) ?
  30664. '-' :
  30665. '') + series.stackKey], pointStack, stackValues;
  30666. // Discard disallowed y values for log axes (#3434)
  30667. if (yAxis.positiveValuesOnly &&
  30668. yValue !== null &&
  30669. yValue <= 0) {
  30670. point.isNull = true;
  30671. }
  30672. // Get the plotX translation
  30673. point.plotX = plotX = correctFloat(// #5236
  30674. limitedRange(xAxis.translate(// #3923
  30675. xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')) // #3923
  30676. );
  30677. // Calculate the bottom y value for stacked series
  30678. if (stacking &&
  30679. series.visible &&
  30680. stack &&
  30681. stack[xValue]) {
  30682. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  30683. if (!point.isNull) {
  30684. pointStack = stack[xValue];
  30685. stackValues =
  30686. pointStack.points[stackIndicator.key];
  30687. }
  30688. }
  30689. if (isArray(stackValues)) {
  30690. yBottom = stackValues[0];
  30691. yValue = stackValues[1];
  30692. if (yBottom === stackThreshold &&
  30693. stackIndicator.key ===
  30694. stack[xValue].base) {
  30695. yBottom = pick((isNumber(threshold) && threshold), yAxis.min);
  30696. }
  30697. // #1200, #1232
  30698. if (yAxis.positiveValuesOnly && yBottom <= 0) {
  30699. yBottom = null;
  30700. }
  30701. point.total = point.stackTotal = pointStack.total;
  30702. point.percentage =
  30703. pointStack.total &&
  30704. (point.y / pointStack.total * 100);
  30705. point.stackY = yValue;
  30706. // Place the stack label
  30707. // in case of variwide series (where widths of points are
  30708. // different in most cases), stack labels are positioned
  30709. // wrongly, so the call of the setOffset is omited here and
  30710. // labels are correctly positioned later, at the end of the
  30711. // variwide's translate function (#10962)
  30712. if (!series.irregularWidths) {
  30713. pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
  30714. }
  30715. }
  30716. // Set translated yBottom or remove it
  30717. point.yBottom = defined(yBottom) ?
  30718. limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
  30719. null;
  30720. // general hook, used for Highstock compare mode
  30721. if (hasModifyValue) {
  30722. yValue = series.modifyValue(yValue, point);
  30723. }
  30724. // Set the the plotY value, reset it for redraws
  30725. // #3201
  30726. point.plotY = plotY = ((typeof yValue === 'number' && yValue !== Infinity) ?
  30727. limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
  30728. void 0);
  30729. point.isInside =
  30730. typeof plotY !== 'undefined' &&
  30731. plotY >= 0 &&
  30732. plotY <= yAxis.len && // #3519
  30733. plotX >= 0 &&
  30734. plotX <= xAxis.len;
  30735. // Set client related positions for mouse tracking
  30736. point.clientX = dynamicallyPlaced ?
  30737. correctFloat(xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)) :
  30738. plotX; // #1514, #5383, #5518
  30739. // Negative points. For bubble charts, this means negative z
  30740. // values (#9728)
  30741. point.negative = point[zoneAxis] < (options[zoneAxis + 'Threshold'] ||
  30742. threshold ||
  30743. 0);
  30744. // some API data
  30745. point.category = (categories &&
  30746. typeof categories[point.x] !== 'undefined' ?
  30747. categories[point.x] :
  30748. point.x);
  30749. // Determine auto enabling of markers (#3635, #5099)
  30750. if (!point.isNull && point.visible !== false) {
  30751. if (typeof lastPlotX !== 'undefined') {
  30752. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  30753. }
  30754. lastPlotX = plotX;
  30755. }
  30756. // Find point zone
  30757. point.zone = (this.zones.length && point.getZone());
  30758. // Animate new points with data sorting
  30759. if (!point.graphic && series.group && enabledDataSorting) {
  30760. point.isNew = true;
  30761. }
  30762. }
  30763. series.closestPointRangePx = closestPointRangePx;
  30764. fireEvent(this, 'afterTranslate');
  30765. },
  30766. /**
  30767. * Return the series points with null points filtered out.
  30768. *
  30769. * @function Highcharts.Series#getValidPoints
  30770. *
  30771. * @param {Array<Highcharts.Point>} [points]
  30772. * The points to inspect, defaults to {@link Series.points}.
  30773. *
  30774. * @param {boolean} [insideOnly=false]
  30775. * Whether to inspect only the points that are inside the visible
  30776. * view.
  30777. *
  30778. * @param {boolean} [allowNull=false]
  30779. * Whether to allow null points to pass as valid points.
  30780. *
  30781. * @return {Array<Highcharts.Point>}
  30782. * The valid points.
  30783. */
  30784. getValidPoints: function (points, insideOnly, allowNull) {
  30785. var chart = this.chart;
  30786. // #3916, #5029, #5085
  30787. return (points || this.points || []).filter(function isValidPoint(point) {
  30788. if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) {
  30789. return false;
  30790. }
  30791. return point.visible !== false &&
  30792. (allowNull || !point.isNull);
  30793. });
  30794. },
  30795. /**
  30796. * Get the clipping for the series. Could be called for a series to
  30797. * initiate animating the clip or to set the final clip (only width
  30798. * and x).
  30799. *
  30800. * @private
  30801. * @function Highcharts.Series#getClip
  30802. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  30803. * Initialize the animation.
  30804. * @param {boolean} [finalBox]
  30805. * Final size for the clip - end state for the animation.
  30806. * @return {Highcharts.Dictionary<number>}
  30807. */
  30808. getClipBox: function (animation, finalBox) {
  30809. var series = this, options = series.options, chart = series.chart, inverted = chart.inverted, xAxis = series.xAxis, yAxis = xAxis && series.yAxis, clipBox;
  30810. if (animation && options.clip === false && yAxis) {
  30811. // support for not clipped series animation (#10450)
  30812. clipBox = inverted ? {
  30813. y: -chart.chartWidth + yAxis.len + yAxis.pos,
  30814. height: chart.chartWidth,
  30815. width: chart.chartHeight,
  30816. x: -chart.chartHeight + xAxis.len + xAxis.pos
  30817. } : {
  30818. y: -yAxis.pos,
  30819. height: chart.chartHeight,
  30820. width: chart.chartWidth,
  30821. x: -xAxis.pos
  30822. };
  30823. // x and width will be changed later when setting for animation
  30824. // initial state in Series.setClip
  30825. }
  30826. else {
  30827. clipBox = series.clipBox || chart.clipBox;
  30828. if (finalBox) {
  30829. clipBox.width = chart.plotSizeX;
  30830. clipBox.x = 0;
  30831. }
  30832. }
  30833. return !finalBox ? clipBox : {
  30834. width: clipBox.width,
  30835. x: clipBox.x
  30836. };
  30837. },
  30838. /**
  30839. * Set the clipping for the series. For animated series it is called
  30840. * twice, first to initiate animating the clip then the second time
  30841. * without the animation to set the final clip.
  30842. *
  30843. * @private
  30844. * @function Highcharts.Series#setClip
  30845. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  30846. * @return {void}
  30847. */
  30848. setClip: function (animation) {
  30849. var chart = this.chart, options = this.options, renderer = chart.renderer, inverted = chart.inverted, seriesClipBox = this.clipBox, clipBox = this.getClipBox(animation), sharedClipKey = this.sharedClipKey ||
  30850. [
  30851. '_sharedClip',
  30852. animation && animation.duration,
  30853. animation && animation.easing,
  30854. clipBox.height,
  30855. options.xAxis,
  30856. options.yAxis
  30857. ].join(','), // #4526
  30858. clipRect = chart[sharedClipKey], markerClipRect = chart[sharedClipKey + 'm'];
  30859. // If a clipping rectangle with the same properties is currently
  30860. // present in the chart, use that.
  30861. if (!clipRect) {
  30862. // When animation is set, prepare the initial positions
  30863. if (animation) {
  30864. clipBox.width = 0;
  30865. if (inverted) {
  30866. clipBox.x = chart.plotSizeX +
  30867. (options.clip !== false ? 0 : chart.plotTop);
  30868. }
  30869. chart[sharedClipKey + 'm'] = markerClipRect =
  30870. renderer.clipRect(
  30871. // include the width of the first marker
  30872. inverted ? chart.plotSizeX + 99 : -99, inverted ? -chart.plotLeft : -chart.plotTop, 99, inverted ? chart.chartWidth : chart.chartHeight);
  30873. }
  30874. chart[sharedClipKey] = clipRect =
  30875. renderer.clipRect(clipBox);
  30876. // Create hashmap for series indexes
  30877. clipRect.count = { length: 0 };
  30878. }
  30879. if (animation) {
  30880. if (!clipRect.count[this.index]) {
  30881. clipRect.count[this.index] = true;
  30882. clipRect.count.length += 1;
  30883. }
  30884. }
  30885. if (options.clip !== false || animation) {
  30886. this.group.clip(animation || seriesClipBox ? clipRect : chart.clipRect);
  30887. this.markerGroup.clip(markerClipRect);
  30888. this.sharedClipKey = sharedClipKey;
  30889. }
  30890. // Remove the shared clipping rectangle when all series are shown
  30891. if (!animation) {
  30892. if (clipRect.count[this.index]) {
  30893. delete clipRect.count[this.index];
  30894. clipRect.count.length -= 1;
  30895. }
  30896. if (clipRect.count.length === 0 &&
  30897. sharedClipKey &&
  30898. chart[sharedClipKey]) {
  30899. if (!seriesClipBox) {
  30900. chart[sharedClipKey] =
  30901. chart[sharedClipKey].destroy();
  30902. }
  30903. if (chart[sharedClipKey + 'm']) {
  30904. chart[sharedClipKey + 'm'] =
  30905. chart[sharedClipKey + 'm'].destroy();
  30906. }
  30907. }
  30908. }
  30909. },
  30910. /**
  30911. * Animate in the series. Called internally twice. First with the `init`
  30912. * parameter set to true, which sets up the initial state of the
  30913. * animation. Then when ready, it is called with the `init` parameter
  30914. * undefined, in order to perform the actual animation. After the
  30915. * second run, the function is removed.
  30916. *
  30917. * @function Highcharts.Series#animate
  30918. *
  30919. * @param {boolean} [init]
  30920. * Initialize the animation.
  30921. *
  30922. * @return {void}
  30923. */
  30924. animate: function (init) {
  30925. var series = this, chart = series.chart, animation = animObject(series.options.animation), clipRect, sharedClipKey, finalBox;
  30926. // Initialize the animation. Set up the clipping rectangle.
  30927. if (init) {
  30928. series.setClip(animation);
  30929. // Run the animation
  30930. }
  30931. else {
  30932. sharedClipKey = this.sharedClipKey;
  30933. clipRect = chart[sharedClipKey];
  30934. finalBox = series.getClipBox(animation, true);
  30935. if (clipRect) {
  30936. clipRect.animate(finalBox, animation);
  30937. }
  30938. if (chart[sharedClipKey + 'm']) {
  30939. chart[sharedClipKey + 'm'].animate({
  30940. width: finalBox.width + 99,
  30941. x: finalBox.x - (chart.inverted ? 0 : 99)
  30942. }, animation);
  30943. }
  30944. // Delete this function to allow it only once
  30945. series.animate = null;
  30946. }
  30947. },
  30948. /**
  30949. * This runs after animation to land on the final plot clipping.
  30950. *
  30951. * @private
  30952. * @function Highcharts.Series#afterAnimate
  30953. * @return {void}
  30954. * @fires Highcharts.Series#event:afterAnimate
  30955. */
  30956. afterAnimate: function () {
  30957. this.setClip();
  30958. fireEvent(this, 'afterAnimate');
  30959. this.finishedAnimating = true;
  30960. },
  30961. /**
  30962. * Draw the markers for line-like series types, and columns or other
  30963. * graphical representation for {@link Point} objects for other series
  30964. * types. The resulting element is typically stored as
  30965. * {@link Point.graphic}, and is created on the first call and updated
  30966. * and moved on subsequent calls.
  30967. *
  30968. * @function Highcharts.Series#drawPoints
  30969. */
  30970. drawPoints: function () {
  30971. 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] ||
  30972. series.markerGroup), xAxis = series.xAxis, markerAttribs, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
  30973. // Use larger or equal as radius is null in bubbles (#6321)
  30974. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  30975. seriesMarkerOptions.radius));
  30976. if (seriesMarkerOptions.enabled !== false ||
  30977. series._hasPointMarkers) {
  30978. for (i = 0; i < points.length; i++) {
  30979. point = points[i];
  30980. graphic = point.graphic;
  30981. verb = graphic ? 'animate' : 'attr';
  30982. pointMarkerOptions = point.marker || {};
  30983. hasPointMarker = !!point.marker;
  30984. var shouldDrawMarker = ((globallyEnabled &&
  30985. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  30986. // only draw the point if y is defined
  30987. if (shouldDrawMarker) {
  30988. // Shortcuts
  30989. var symbol = pick(pointMarkerOptions.symbol, series.symbol);
  30990. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  30991. // Set starting position for point sliding animation.
  30992. if (series.enabledDataSorting) {
  30993. point.startXPos = xAxis.reversed ?
  30994. -markerAttribs.width :
  30995. xAxis.width;
  30996. }
  30997. var isInside = point.isInside !== false;
  30998. if (graphic) { // update
  30999. // Since the marker group isn't clipped, each
  31000. // individual marker must be toggled
  31001. graphic[isInside ? 'show' : 'hide'](isInside)
  31002. .animate(markerAttribs);
  31003. }
  31004. else if (isInside &&
  31005. (markerAttribs.width > 0 || point.hasImage)) {
  31006. /**
  31007. * The graphic representation of the point.
  31008. * Typically this is a simple shape, like a `rect`
  31009. * for column charts or `path` for line markers, but
  31010. * for some complex series types like boxplot or 3D
  31011. * charts, the graphic may be a `g` element
  31012. * containing other shapes. The graphic is generated
  31013. * the first time {@link Series#drawPoints} runs,
  31014. * and updated and moved on subsequent runs.
  31015. *
  31016. * @name Point#graphic
  31017. * @type {SVGElement}
  31018. */
  31019. point.graphic = graphic = chart.renderer
  31020. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  31021. pointMarkerOptions :
  31022. seriesMarkerOptions)
  31023. .add(markerGroup);
  31024. // Sliding animation for new points
  31025. if (series.enabledDataSorting &&
  31026. chart.hasRendered) {
  31027. graphic.attr({
  31028. x: point.startXPos
  31029. });
  31030. verb = 'animate';
  31031. }
  31032. }
  31033. if (graphic && verb === 'animate') { // update
  31034. // Since the marker group isn't clipped, each
  31035. // individual marker must be toggled
  31036. graphic[isInside ? 'show' : 'hide'](isInside)
  31037. .animate(markerAttribs);
  31038. }
  31039. // Presentational attributes
  31040. if (graphic && !chart.styledMode) {
  31041. graphic[verb](series.pointAttribs(point, (point.selected && 'select')));
  31042. }
  31043. if (graphic) {
  31044. graphic.addClass(point.getClassName(), true);
  31045. }
  31046. }
  31047. else if (graphic) {
  31048. point.graphic = graphic.destroy(); // #1269
  31049. }
  31050. }
  31051. }
  31052. },
  31053. /**
  31054. * Get non-presentational attributes for a point. Used internally for
  31055. * both styled mode and classic. Can be overridden for different series
  31056. * types.
  31057. *
  31058. * @see Series#pointAttribs
  31059. *
  31060. * @function Highcharts.Series#markerAttribs
  31061. *
  31062. * @param {Highcharts.Point} point
  31063. * The Point to inspect.
  31064. *
  31065. * @param {string} [state]
  31066. * The state, can be either `hover`, `select` or undefined.
  31067. *
  31068. * @return {Highcharts.SVGAttributes}
  31069. * A hash containing those attributes that are not settable from
  31070. * CSS.
  31071. */
  31072. markerAttribs: function (point, state) {
  31073. var seriesMarkerOptions = this.options.marker, seriesStateOptions, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
  31074. seriesMarkerOptions.symbol), pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions.radius), attribs;
  31075. // Handle hover and select states
  31076. if (state) {
  31077. seriesStateOptions = seriesMarkerOptions.states[state];
  31078. pointStateOptions = pointMarkerOptions.states &&
  31079. pointMarkerOptions.states[state];
  31080. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  31081. 0));
  31082. }
  31083. point.hasImage = symbol && symbol.indexOf('url') === 0;
  31084. if (point.hasImage) {
  31085. radius = 0; // and subsequently width and height is not set
  31086. }
  31087. attribs = {
  31088. // Math.floor for #1843:
  31089. x: Math.floor(point.plotX) - radius,
  31090. y: point.plotY - radius
  31091. };
  31092. if (radius) {
  31093. attribs.width = attribs.height = 2 * radius;
  31094. }
  31095. return attribs;
  31096. },
  31097. /**
  31098. * Internal function to get presentational attributes for each point.
  31099. * Unlike {@link Series#markerAttribs}, this function should return
  31100. * those attributes that can also be set in CSS. In styled mode,
  31101. * `pointAttribs` won't be called.
  31102. *
  31103. * @private
  31104. * @function Highcharts.Series#pointAttribs
  31105. *
  31106. * @param {Highcharts.Point} [point]
  31107. * The point instance to inspect.
  31108. *
  31109. * @param {string} [state]
  31110. * The point state, can be either `hover`, `select` or 'normal'.
  31111. * If undefined, normal state is assumed.
  31112. *
  31113. * @return {Highcharts.SVGAttributes}
  31114. * The presentational attributes to be set on the point.
  31115. */
  31116. pointAttribs: function (point, state) {
  31117. 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;
  31118. color = (pointColorOption ||
  31119. zoneColor ||
  31120. pointColor ||
  31121. color);
  31122. fill = (pointMarkerOptions.fillColor ||
  31123. seriesMarkerOptions.fillColor ||
  31124. color);
  31125. stroke = (pointMarkerOptions.lineColor ||
  31126. seriesMarkerOptions.lineColor ||
  31127. color);
  31128. // Handle hover and select states
  31129. state = state || 'normal';
  31130. if (state) {
  31131. seriesStateOptions = seriesMarkerOptions.states[state];
  31132. pointStateOptions = (pointMarkerOptions.states &&
  31133. pointMarkerOptions.states[state]) || {};
  31134. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  31135. fill = (pointStateOptions.fillColor ||
  31136. seriesStateOptions.fillColor ||
  31137. fill);
  31138. stroke = (pointStateOptions.lineColor ||
  31139. seriesStateOptions.lineColor ||
  31140. stroke);
  31141. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  31142. }
  31143. return {
  31144. 'stroke': stroke,
  31145. 'stroke-width': strokeWidth,
  31146. 'fill': fill,
  31147. 'opacity': opacity
  31148. };
  31149. },
  31150. /**
  31151. * Clear DOM objects and free up memory.
  31152. *
  31153. * @private
  31154. * @function Highcharts.Series#destroy
  31155. * @param {boolean} [keepEventsForUpdate]
  31156. * @return {void}
  31157. * @fires Highcharts.Series#event:destroy
  31158. */
  31159. destroy: function (keepEventsForUpdate) {
  31160. var series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), destroy, i, data = series.data || [], point, axis;
  31161. // add event hook
  31162. fireEvent(series, 'destroy');
  31163. // remove events
  31164. this.removeEvents(keepEventsForUpdate);
  31165. // erase from axes
  31166. (series.axisTypes || []).forEach(function (AXIS) {
  31167. axis = series[AXIS];
  31168. if (axis && axis.series) {
  31169. erase(axis.series, series);
  31170. axis.isDirty = axis.forceRedraw = true;
  31171. }
  31172. });
  31173. // remove legend items
  31174. if (series.legendItem) {
  31175. series.chart.legend.destroyItem(series);
  31176. }
  31177. // destroy all points with their elements
  31178. i = data.length;
  31179. while (i--) {
  31180. point = data[i];
  31181. if (point && point.destroy) {
  31182. point.destroy();
  31183. }
  31184. }
  31185. series.points = null;
  31186. // Clear the animation timeout if we are destroying the series
  31187. // during initial animation
  31188. H.clearTimeout(series.animationTimeout);
  31189. // Destroy all SVGElements associated to the series
  31190. objectEach(series, function (val, prop) {
  31191. // Survive provides a hook for not destroying
  31192. if (val instanceof SVGElement && !val.survive) {
  31193. // issue 134 workaround
  31194. destroy = issue134 && prop === 'group' ?
  31195. 'hide' :
  31196. 'destroy';
  31197. val[destroy]();
  31198. }
  31199. });
  31200. // remove from hoverSeries
  31201. if (chart.hoverSeries === series) {
  31202. chart.hoverSeries = null;
  31203. }
  31204. erase(chart.series, series);
  31205. chart.orderSeries();
  31206. // clear all members
  31207. objectEach(series, function (val, prop) {
  31208. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  31209. delete series[prop];
  31210. }
  31211. });
  31212. },
  31213. /**
  31214. * Get the graph path.
  31215. *
  31216. * @private
  31217. * @function Highcharts.Series#getGraphPath
  31218. * @param {Array<Highcharts.Point>} points
  31219. * @param {boolean} [nullsAsZeroes]
  31220. * @param {boolean} [connectCliffs]
  31221. * @return {Highcharts.SVGPathArray}
  31222. */
  31223. getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
  31224. var series = this, options = series.options, step = options.step, reversed, graphPath = [], xMap = [], gap;
  31225. points = points || series.points;
  31226. // Bottom of a stack is reversed
  31227. reversed = points.reversed;
  31228. if (reversed) {
  31229. points.reverse();
  31230. }
  31231. // Reverse the steps (#5004)
  31232. step = {
  31233. right: 1,
  31234. center: 2
  31235. }[step] || (step && 3);
  31236. if (step && reversed) {
  31237. step = 4 - step;
  31238. }
  31239. // Remove invalid points, especially in spline (#5015)
  31240. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  31241. // Build the line
  31242. points.forEach(function (point, i) {
  31243. var plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1],
  31244. // the path to this point from the previous
  31245. pathToPoint;
  31246. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  31247. !connectCliffs) {
  31248. gap = true; // ... and continue
  31249. }
  31250. // Line series, nullsAsZeroes is not handled
  31251. if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
  31252. gap = !options.connectNulls;
  31253. // Area series, nullsAsZeroes is set
  31254. }
  31255. else if (point.isNull && !nullsAsZeroes) {
  31256. gap = true;
  31257. }
  31258. else {
  31259. if (i === 0 || gap) {
  31260. pathToPoint = [
  31261. 'M',
  31262. point.plotX,
  31263. point.plotY
  31264. ];
  31265. // Generate the spline as defined in the SplineSeries object
  31266. }
  31267. else if (series.getPointSpline) {
  31268. pathToPoint = series.getPointSpline(points, point, i);
  31269. }
  31270. else if (step) {
  31271. if (step === 1) { // right
  31272. pathToPoint = [
  31273. 'L',
  31274. lastPoint.plotX,
  31275. plotY
  31276. ];
  31277. }
  31278. else if (step === 2) { // center
  31279. pathToPoint = [
  31280. 'L',
  31281. (lastPoint.plotX + plotX) / 2,
  31282. lastPoint.plotY,
  31283. 'L',
  31284. (lastPoint.plotX + plotX) / 2,
  31285. plotY
  31286. ];
  31287. }
  31288. else {
  31289. pathToPoint = [
  31290. 'L',
  31291. plotX,
  31292. lastPoint.plotY
  31293. ];
  31294. }
  31295. pathToPoint.push('L', plotX, plotY);
  31296. }
  31297. else {
  31298. // normal line to next point
  31299. pathToPoint = [
  31300. 'L',
  31301. plotX,
  31302. plotY
  31303. ];
  31304. }
  31305. // Prepare for animation. When step is enabled, there are
  31306. // two path nodes for each x value.
  31307. xMap.push(point.x);
  31308. if (step) {
  31309. xMap.push(point.x);
  31310. if (step === 2) { // step = center (#8073)
  31311. xMap.push(point.x);
  31312. }
  31313. }
  31314. graphPath.push.apply(graphPath, pathToPoint);
  31315. gap = false;
  31316. }
  31317. });
  31318. graphPath.xMap = xMap;
  31319. series.graphPath = graphPath;
  31320. return graphPath;
  31321. },
  31322. /**
  31323. * Draw the graph. Called internally when rendering line-like series
  31324. * types. The first time it generates the `series.graph` item and
  31325. * optionally other series-wide items like `series.area` for area
  31326. * charts. On subsequent calls these items are updated with new
  31327. * positions and attributes.
  31328. *
  31329. * @function Highcharts.Series#drawGraph
  31330. *
  31331. * @return {void}
  31332. */
  31333. drawGraph: function () {
  31334. var series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode, props = [[
  31335. 'graph',
  31336. 'highcharts-graph'
  31337. ]];
  31338. // Presentational properties
  31339. if (!styledMode) {
  31340. props[0].push((options.lineColor ||
  31341. this.color ||
  31342. '#cccccc' // when colorByPoint = true
  31343. ), options.dashStyle);
  31344. }
  31345. props = series.getZonesGraphs(props);
  31346. // Draw the graph
  31347. props.forEach(function (prop, i) {
  31348. var graphKey = prop[0], graph = series[graphKey], verb = graph ? 'animate' : 'attr', attribs;
  31349. if (graph) {
  31350. graph.endX = series.preventGraphAnimation ?
  31351. null :
  31352. graphPath.xMap;
  31353. graph.animate({ d: graphPath });
  31354. }
  31355. else if (graphPath.length) { // #1487
  31356. /**
  31357. * SVG element of area-based charts. Can be used for styling
  31358. * purposes. If zones are configured, this element will be
  31359. * hidden and replaced by multiple zone areas, accessible
  31360. * via `series['zone-area-x']` (where x is a number,
  31361. * starting with 0).
  31362. *
  31363. * @name Highcharts.Series#area
  31364. * @type {Highcharts.SVGElement|undefined}
  31365. */
  31366. /**
  31367. * SVG element of line-based charts. Can be used for styling
  31368. * purposes. If zones are configured, this element will be
  31369. * hidden and replaced by multiple zone lines, accessible
  31370. * via `series['zone-graph-x']` (where x is a number,
  31371. * starting with 0).
  31372. *
  31373. * @name Highcharts.Series#graph
  31374. * @type {Highcharts.SVGElement|undefined}
  31375. */
  31376. series[graphKey] = graph = series.chart.renderer
  31377. .path(graphPath)
  31378. .addClass(prop[1])
  31379. .attr({ zIndex: 1 }) // #1069
  31380. .add(series.group);
  31381. }
  31382. if (graph && !styledMode) {
  31383. attribs = {
  31384. 'stroke': prop[2],
  31385. 'stroke-width': options.lineWidth,
  31386. // Polygon series use filled graph
  31387. 'fill': (series.fillGraph && series.color) || 'none'
  31388. };
  31389. if (prop[3]) {
  31390. attribs.dashstyle = prop[3];
  31391. }
  31392. else if (options.linecap !== 'square') {
  31393. attribs['stroke-linecap'] =
  31394. attribs['stroke-linejoin'] = 'round';
  31395. }
  31396. graph[verb](attribs)
  31397. // Add shadow to normal series (0) or to first
  31398. // zone (1) #3932
  31399. .shadow((i < 2) && options.shadow);
  31400. }
  31401. // Helpers for animation
  31402. if (graph) {
  31403. graph.startX = graphPath.xMap;
  31404. graph.isArea = graphPath.isArea; // For arearange animation
  31405. }
  31406. });
  31407. },
  31408. /**
  31409. * Get zones properties for building graphs. Extendable by series with
  31410. * multiple lines within one series.
  31411. *
  31412. * @private
  31413. * @function Highcharts.Series#getZonesGraphs
  31414. *
  31415. * @param {Array<Array<string>>} props
  31416. *
  31417. * @return {Array<Array<string>>}
  31418. */
  31419. getZonesGraphs: function (props) {
  31420. // Add the zone properties if any
  31421. this.zones.forEach(function (zone, i) {
  31422. var propset = [
  31423. 'zone-graph-' + i,
  31424. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  31425. (zone.className || '')
  31426. ];
  31427. if (!this.chart.styledMode) {
  31428. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  31429. }
  31430. props.push(propset);
  31431. }, this);
  31432. return props;
  31433. },
  31434. /**
  31435. * Clip the graphs into zones for colors and styling.
  31436. *
  31437. * @private
  31438. * @function Highcharts.Series#applyZones
  31439. * @return {void}
  31440. */
  31441. applyZones: function () {
  31442. 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;
  31443. if (zones.length &&
  31444. (graph || area) &&
  31445. axis &&
  31446. typeof axis.min !== 'undefined') {
  31447. reversed = axis.reversed;
  31448. horiz = axis.horiz;
  31449. // The use of the Color Threshold assumes there are no gaps
  31450. // so it is safe to hide the original graph and area
  31451. // unless it is not waterfall series, then use showLine property
  31452. // to set lines between columns to be visible (#7862)
  31453. if (graph && !this.showLine) {
  31454. graph.hide();
  31455. }
  31456. if (area) {
  31457. area.hide();
  31458. }
  31459. // Create the clips
  31460. extremes = axis.getExtremes();
  31461. zones.forEach(function (threshold, i) {
  31462. translatedFrom = reversed ?
  31463. (horiz ? chart.plotWidth : 0) :
  31464. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  31465. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, chartSizeMax);
  31466. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, chartSizeMax);
  31467. if (ignoreZones) {
  31468. translatedFrom = translatedTo =
  31469. axis.toPixels(extremes.max);
  31470. }
  31471. pxRange = Math.abs(translatedFrom - translatedTo);
  31472. pxPosMin = Math.min(translatedFrom, translatedTo);
  31473. pxPosMax = Math.max(translatedFrom, translatedTo);
  31474. if (axis.isXAxis) {
  31475. clipAttr = {
  31476. x: inverted ? pxPosMax : pxPosMin,
  31477. y: 0,
  31478. width: pxRange,
  31479. height: chartSizeMax
  31480. };
  31481. if (!horiz) {
  31482. clipAttr.x = chart.plotHeight - clipAttr.x;
  31483. }
  31484. }
  31485. else {
  31486. clipAttr = {
  31487. x: 0,
  31488. y: inverted ? pxPosMax : pxPosMin,
  31489. width: chartSizeMax,
  31490. height: pxRange
  31491. };
  31492. if (horiz) {
  31493. clipAttr.y = chart.plotWidth - clipAttr.y;
  31494. }
  31495. }
  31496. // VML SUPPPORT
  31497. if (inverted && renderer.isVML) {
  31498. if (axis.isXAxis) {
  31499. clipAttr = {
  31500. x: 0,
  31501. y: reversed ? pxPosMin : pxPosMax,
  31502. height: clipAttr.width,
  31503. width: chart.chartWidth
  31504. };
  31505. }
  31506. else {
  31507. clipAttr = {
  31508. x: (clipAttr.y -
  31509. chart.plotLeft -
  31510. chart.spacingBox.x),
  31511. y: 0,
  31512. width: clipAttr.height,
  31513. height: chart.chartHeight
  31514. };
  31515. }
  31516. }
  31517. // END OF VML SUPPORT
  31518. if (clips[i]) {
  31519. clips[i].animate(clipAttr);
  31520. }
  31521. else {
  31522. clips[i] = renderer.clipRect(clipAttr);
  31523. }
  31524. // when no data, graph zone is not applied and after setData
  31525. // clip was ignored. As a result, it should be applied each
  31526. // time.
  31527. if (graph) {
  31528. series['zone-graph-' + i].clip(clips[i]);
  31529. }
  31530. if (area) {
  31531. series['zone-area-' + i].clip(clips[i]);
  31532. }
  31533. // if this zone extends out of the axis, ignore the others
  31534. ignoreZones = threshold.value > extremes.max;
  31535. // Clear translatedTo for indicators
  31536. if (series.resetZones && translatedTo === 0) {
  31537. translatedTo = void 0;
  31538. }
  31539. });
  31540. this.clips = clips;
  31541. }
  31542. else if (series.visible) {
  31543. // If zones were removed, restore graph and area
  31544. if (graph) {
  31545. graph.show(true);
  31546. }
  31547. if (area) {
  31548. area.show(true);
  31549. }
  31550. }
  31551. },
  31552. /**
  31553. * Initialize and perform group inversion on series.group and
  31554. * series.markerGroup.
  31555. *
  31556. * @private
  31557. * @function Highcharts.Series#invertGroups
  31558. * @param {boolean} [inverted]
  31559. * @return {void}
  31560. */
  31561. invertGroups: function (inverted) {
  31562. var series = this, chart = series.chart;
  31563. /**
  31564. * @private
  31565. */
  31566. function setInvert() {
  31567. ['group', 'markerGroup'].forEach(function (groupName) {
  31568. if (series[groupName]) {
  31569. // VML/HTML needs explicit attributes for flipping
  31570. if (chart.renderer.isVML) {
  31571. series[groupName].attr({
  31572. width: series.yAxis.len,
  31573. height: series.xAxis.len
  31574. });
  31575. }
  31576. series[groupName].width = series.yAxis.len;
  31577. series[groupName].height = series.xAxis.len;
  31578. // If inverted polar, don't invert series group
  31579. series[groupName].invert(series.isRadialSeries ? false : inverted);
  31580. }
  31581. });
  31582. }
  31583. // Pie, go away (#1736)
  31584. if (!series.xAxis) {
  31585. return;
  31586. }
  31587. // A fixed size is needed for inversion to work
  31588. series.eventsToUnbind.push(addEvent(chart, 'resize', setInvert));
  31589. // Do it now
  31590. setInvert();
  31591. // On subsequent render and redraw, just do setInvert without
  31592. // setting up events again
  31593. series.invertGroups = setInvert;
  31594. },
  31595. /**
  31596. * General abstraction for creating plot groups like series.group,
  31597. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  31598. * the group will only be adjusted to the updated plot size.
  31599. *
  31600. * @private
  31601. * @function Highcharts.Series#plotGroup
  31602. * @param {string} prop
  31603. * @param {string} name
  31604. * @param {string} visibility
  31605. * @param {number} [zIndex]
  31606. * @param {Highcharts.SVGElement} [parent]
  31607. * @return {Highcharts.SVGElement}
  31608. */
  31609. plotGroup: function (prop, name, visibility, zIndex, parent) {
  31610. var group = this[prop], isNew = !group;
  31611. // Generate it on first call
  31612. if (isNew) {
  31613. this[prop] = group = this.chart.renderer
  31614. .g()
  31615. .attr({
  31616. zIndex: zIndex || 0.1 // IE8 and pointer logic use this
  31617. })
  31618. .add(parent);
  31619. }
  31620. // Add the class names, and replace existing ones as response to
  31621. // Series.update (#6660)
  31622. group.addClass(('highcharts-' + name +
  31623. ' highcharts-series-' + this.index +
  31624. ' highcharts-' + this.type + '-series ' +
  31625. (defined(this.colorIndex) ?
  31626. 'highcharts-color-' + this.colorIndex + ' ' :
  31627. '') +
  31628. (this.options.className || '') +
  31629. (group.hasClass('highcharts-tracker') ?
  31630. ' highcharts-tracker' :
  31631. '')), true);
  31632. // Place it on first and subsequent (redraw) calls
  31633. group.attr({ visibility: visibility })[isNew ? 'attr' : 'animate'](this.getPlotBox());
  31634. return group;
  31635. },
  31636. /**
  31637. * Get the translation and scale for the plot area of this series.
  31638. *
  31639. * @function Highcharts.Series#getPlotBox
  31640. *
  31641. * @return {Highcharts.SeriesPlotBoxObject}
  31642. */
  31643. getPlotBox: function () {
  31644. var chart = this.chart, xAxis = this.xAxis, yAxis = this.yAxis;
  31645. // Swap axes for inverted (#2339)
  31646. if (chart.inverted) {
  31647. xAxis = yAxis;
  31648. yAxis = this.xAxis;
  31649. }
  31650. return {
  31651. translateX: xAxis ? xAxis.left : chart.plotLeft,
  31652. translateY: yAxis ? yAxis.top : chart.plotTop,
  31653. scaleX: 1,
  31654. scaleY: 1
  31655. };
  31656. },
  31657. /**
  31658. * Removes the event handlers attached previously with addEvents.
  31659. *
  31660. * @private
  31661. * @function Highcharts.Series#removeEvents
  31662. * @param {boolean} [keepEventsForUpdate]
  31663. * @return {void}
  31664. */
  31665. removeEvents: function (keepEventsForUpdate) {
  31666. var series = this;
  31667. if (!keepEventsForUpdate) {
  31668. // remove all events
  31669. removeEvent(series);
  31670. }
  31671. else if (series.eventsToUnbind.length) {
  31672. // remove only internal events for proper update
  31673. // #12355 - solves problem with multiple destroy events
  31674. series.eventsToUnbind.forEach(function (unbind) {
  31675. unbind();
  31676. });
  31677. series.eventsToUnbind.length = 0;
  31678. }
  31679. },
  31680. /**
  31681. * Render the graph and markers. Called internally when first rendering
  31682. * and later when redrawing the chart. This function can be extended in
  31683. * plugins, but normally shouldn't be called directly.
  31684. *
  31685. * @function Highcharts.Series#render
  31686. *
  31687. * @return {void}
  31688. *
  31689. * @fires Highcharts.Series#event:afterRender
  31690. */
  31691. render: function () {
  31692. var series = this, chart = series.chart, group, options = series.options,
  31693. // Animation doesn't work in IE8 quirks when the group div is
  31694. // hidden, and looks bad in other oldIE
  31695. animDuration = (!!series.animate &&
  31696. chart.renderer.isSVG &&
  31697. animObject(options.animation).duration), visibility = series.visible ? 'inherit' : 'hidden', // #2597
  31698. zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
  31699. fireEvent(this, 'render');
  31700. // the group
  31701. group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  31702. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  31703. // initiate the animation
  31704. if (animDuration) {
  31705. series.animate(true);
  31706. }
  31707. // SVGRenderer needs to know this before drawing elements (#1089,
  31708. // #1795)
  31709. group.inverted = series.isCartesian || series.invertable ?
  31710. inverted : false;
  31711. // Draw the graph if any
  31712. if (series.drawGraph) {
  31713. series.drawGraph();
  31714. series.applyZones();
  31715. }
  31716. // Draw the points
  31717. if (series.visible) {
  31718. series.drawPoints();
  31719. }
  31720. /* series.points.forEach(function (point) {
  31721. if (point.redraw) {
  31722. point.redraw();
  31723. }
  31724. }); */
  31725. // Draw the data labels
  31726. if (series.drawDataLabels) {
  31727. series.drawDataLabels();
  31728. }
  31729. // In pie charts, slices are added to the DOM, but actual rendering
  31730. // is postponed until labels reserved their space
  31731. if (series.redrawPoints) {
  31732. series.redrawPoints();
  31733. }
  31734. // draw the mouse tracking area
  31735. if (series.drawTracker &&
  31736. series.options.enableMouseTracking !== false) {
  31737. series.drawTracker();
  31738. }
  31739. // Handle inverted series and tracker groups
  31740. series.invertGroups(inverted);
  31741. // Initial clipping, must be defined after inverting groups for VML.
  31742. // Applies to columns etc. (#3839).
  31743. if (options.clip !== false &&
  31744. !series.sharedClipKey &&
  31745. !hasRendered) {
  31746. group.clip(chart.clipRect);
  31747. }
  31748. // Run the animation
  31749. if (animDuration) {
  31750. series.animate();
  31751. }
  31752. // Call the afterAnimate function on animation complete (but don't
  31753. // overwrite the animation.complete option which should be available
  31754. // to the user).
  31755. if (!hasRendered) {
  31756. series.animationTimeout = syncTimeout(function () {
  31757. series.afterAnimate();
  31758. }, animDuration || 0);
  31759. }
  31760. // Means data is in accordance with what you see
  31761. series.isDirty = false;
  31762. // (See #322) series.isDirty = series.isDirtyData = false; // means
  31763. // data is in accordance with what you see
  31764. series.hasRendered = true;
  31765. fireEvent(series, 'afterRender');
  31766. },
  31767. /**
  31768. * Redraw the series. This function is called internally from
  31769. * `chart.redraw` and normally shouldn't be called directly.
  31770. *
  31771. * @private
  31772. * @function Highcharts.Series#redraw
  31773. * @return {void}
  31774. */
  31775. redraw: function () {
  31776. var series = this, chart = series.chart,
  31777. // cache it here as it is set to false in render, but used after
  31778. wasDirty = series.isDirty || series.isDirtyData, group = series.group, xAxis = series.xAxis, yAxis = series.yAxis;
  31779. // reposition on resize
  31780. if (group) {
  31781. if (chart.inverted) {
  31782. group.attr({
  31783. width: chart.plotWidth,
  31784. height: chart.plotHeight
  31785. });
  31786. }
  31787. group.animate({
  31788. translateX: pick(xAxis && xAxis.left, chart.plotLeft),
  31789. translateY: pick(yAxis && yAxis.top, chart.plotTop)
  31790. });
  31791. }
  31792. series.translate();
  31793. series.render();
  31794. if (wasDirty) { // #3868, #3945
  31795. delete this.kdTree;
  31796. }
  31797. },
  31798. kdAxisArray: ['clientX', 'plotY'],
  31799. /**
  31800. * @private
  31801. * @function Highcharts.Series#searchPoint
  31802. * @param {Highcharts.PointerEventObject} e
  31803. * @param {boolean} [compareX]
  31804. * @return {Highcharts.Point}
  31805. */
  31806. searchPoint: function (e, compareX) {
  31807. var series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
  31808. return this.searchKDTree({
  31809. clientX: inverted ?
  31810. xAxis.len - e.chartY + xAxis.pos :
  31811. e.chartX - xAxis.pos,
  31812. plotY: inverted ?
  31813. yAxis.len - e.chartX + yAxis.pos :
  31814. e.chartY - yAxis.pos
  31815. }, compareX, e);
  31816. },
  31817. /**
  31818. * Build the k-d-tree that is used by mouse and touch interaction to get
  31819. * the closest point. Line-like series typically have a one-dimensional
  31820. * tree where points are searched along the X axis, while scatter-like
  31821. * series typically search in two dimensions, X and Y.
  31822. *
  31823. * @private
  31824. * @function Highcharts.Series#buildKDTree
  31825. * @param {Highcharts.PointerEventObject} [e]
  31826. * @return {void}
  31827. */
  31828. buildKDTree: function (e) {
  31829. // Prevent multiple k-d-trees from being built simultaneously
  31830. // (#6235)
  31831. this.buildingKdTree = true;
  31832. var series = this, dimensions = series.options.findNearestPointBy
  31833. .indexOf('y') > -1 ? 2 : 1;
  31834. /**
  31835. * Internal function
  31836. * @private
  31837. */
  31838. function _kdtree(points, depth, dimensions) {
  31839. var axis, median, length = points && points.length;
  31840. if (length) {
  31841. // alternate between the axis
  31842. axis = series.kdAxisArray[depth % dimensions];
  31843. // sort point array
  31844. points.sort(function (a, b) {
  31845. return a[axis] - b[axis];
  31846. });
  31847. median = Math.floor(length / 2);
  31848. // build and return nod
  31849. return {
  31850. point: points[median],
  31851. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  31852. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  31853. };
  31854. }
  31855. }
  31856. /**
  31857. * Start the recursive build process with a clone of the points
  31858. * array and null points filtered out. (#3873)
  31859. * @private
  31860. */
  31861. function startRecursive() {
  31862. series.kdTree = _kdtree(series.getValidPoints(null,
  31863. // For line-type series restrict to plot area, but
  31864. // column-type series not (#3916, #4511)
  31865. !series.directTouch), dimensions, dimensions);
  31866. series.buildingKdTree = false;
  31867. }
  31868. delete series.kdTree;
  31869. // For testing tooltips, don't build async. Also if touchstart, we
  31870. // may be dealing with click events on mobile, so don't delay
  31871. // (#6817).
  31872. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  31873. },
  31874. /**
  31875. * @private
  31876. * @function Highcharts.Series#searchKDTree
  31877. * @param {Highcharts.KDPointSearchObject} point
  31878. * @param {boolean} [compareX]
  31879. * @param {Highcharts.PointerEventObject} [e]
  31880. * @return {Highcharts.Point|undefined}
  31881. */
  31882. searchKDTree: function (point, compareX, e) {
  31883. var series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
  31884. .indexOf('y') > -1 ? 2 : 1;
  31885. /**
  31886. * Set the one and two dimensional distance on the point object.
  31887. * @private
  31888. */
  31889. function setDistance(p1, p2) {
  31890. var x = (defined(p1[kdX]) &&
  31891. defined(p2[kdX])) ?
  31892. Math.pow(p1[kdX] - p2[kdX], 2) :
  31893. null, y = (defined(p1[kdY]) &&
  31894. defined(p2[kdY])) ?
  31895. Math.pow(p1[kdY] - p2[kdY], 2) :
  31896. null, r = (x || 0) + (y || 0);
  31897. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  31898. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  31899. }
  31900. /**
  31901. * @private
  31902. */
  31903. function _search(search, tree, depth, dimensions) {
  31904. var point = tree.point, axis = series.kdAxisArray[depth % dimensions], tdist, sideA, sideB, ret = point, nPoint1, nPoint2;
  31905. setDistance(search, point);
  31906. // Pick side based on distance to splitting point
  31907. tdist = search[axis] - point[axis];
  31908. sideA = tdist < 0 ? 'left' : 'right';
  31909. sideB = tdist < 0 ? 'right' : 'left';
  31910. // End of tree
  31911. if (tree[sideA]) {
  31912. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  31913. ret = (nPoint1[kdComparer] <
  31914. ret[kdComparer] ?
  31915. nPoint1 :
  31916. point);
  31917. }
  31918. if (tree[sideB]) {
  31919. // compare distance to current best to splitting point to
  31920. // decide wether to check side B or not
  31921. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  31922. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  31923. ret = (nPoint2[kdComparer] <
  31924. ret[kdComparer] ?
  31925. nPoint2 :
  31926. ret);
  31927. }
  31928. }
  31929. return ret;
  31930. }
  31931. if (!this.kdTree && !this.buildingKdTree) {
  31932. this.buildKDTree(e);
  31933. }
  31934. if (this.kdTree) {
  31935. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  31936. }
  31937. },
  31938. /**
  31939. * @private
  31940. * @function Highcharts.Series#pointPlacementToXValue
  31941. * @return {number}
  31942. */
  31943. pointPlacementToXValue: function () {
  31944. var series = this, axis = series.xAxis, pointPlacement = series.options.pointPlacement;
  31945. // Point placement is relative to each series pointRange (#5889)
  31946. if (pointPlacement === 'between') {
  31947. pointPlacement = axis.reversed ? -0.5 : 0.5; // #11955
  31948. }
  31949. if (isNumber(pointPlacement)) {
  31950. pointPlacement *=
  31951. pick(series.options.pointRange || axis.pointRange);
  31952. }
  31953. return pointPlacement;
  31954. }
  31955. }); // end Series prototype
  31956. /**
  31957. * A line series displays information as a series of data points connected by
  31958. * straight line segments.
  31959. *
  31960. * @sample {highcharts} highcharts/demo/line-basic/
  31961. * Line chart
  31962. * @sample {highstock} stock/demo/basic-line/
  31963. * Line chart
  31964. *
  31965. * @extends plotOptions.series
  31966. * @product highcharts highstock
  31967. * @apioption plotOptions.line
  31968. */
  31969. /**
  31970. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  31971. * of a line graph. Round means that lines are rounded in the ends and
  31972. * bends.
  31973. *
  31974. * @type {Highcharts.SeriesLinecapValue}
  31975. * @default round
  31976. * @since 3.0.7
  31977. * @apioption plotOptions.line.linecap
  31978. */
  31979. /**
  31980. * A `line` series. If the [type](#series.line.type) option is not
  31981. * specified, it is inherited from [chart.type](#chart.type).
  31982. *
  31983. * @extends series,plotOptions.line
  31984. * @excluding dataParser,dataURL
  31985. * @product highcharts highstock
  31986. * @apioption series.line
  31987. */
  31988. /**
  31989. * An array of data points for the series. For the `line` series type,
  31990. * points can be given in the following ways:
  31991. *
  31992. * 1. An array of numerical values. In this case, the numerical values will be
  31993. * interpreted as `y` options. The `x` values will be automatically
  31994. * calculated, either starting at 0 and incremented by 1, or from
  31995. * `pointStart` and `pointInterval` given in the series options. If the axis
  31996. * has categories, these will be used. Example:
  31997. * ```js
  31998. * data: [0, 5, 3, 5]
  31999. * ```
  32000. *
  32001. * 2. An array of arrays with 2 values. In this case, the values correspond to
  32002. * `x,y`. If the first value is a string, it is applied as the name of the
  32003. * point, and the `x` value is inferred.
  32004. * ```js
  32005. * data: [
  32006. * [0, 1],
  32007. * [1, 2],
  32008. * [2, 8]
  32009. * ]
  32010. * ```
  32011. *
  32012. * 3. An array of objects with named values. The following snippet shows only a
  32013. * few settings, see the complete options set below. If the total number of
  32014. * data points exceeds the series'
  32015. * [turboThreshold](#series.line.turboThreshold),
  32016. * this option is not available.
  32017. * ```js
  32018. * data: [{
  32019. * x: 1,
  32020. * y: 9,
  32021. * name: "Point2",
  32022. * color: "#00FF00"
  32023. * }, {
  32024. * x: 1,
  32025. * y: 6,
  32026. * name: "Point1",
  32027. * color: "#FF00FF"
  32028. * }]
  32029. * ```
  32030. *
  32031. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  32032. * additional declaration to allow custom data options:
  32033. * ```ts
  32034. * declare module `highcharts` {
  32035. * interface PointOptionsObject {
  32036. * customProperty: string;
  32037. * }
  32038. * }
  32039. * ```
  32040. *
  32041. * @sample {highcharts} highcharts/chart/reflow-true/
  32042. * Numerical values
  32043. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  32044. * Arrays of numeric x and y
  32045. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  32046. * Arrays of datetime x and y
  32047. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  32048. * Arrays of point.name and y
  32049. * @sample {highcharts} highcharts/series/data-array-of-objects/
  32050. * Config objects
  32051. *
  32052. * @declare Highcharts.PointOptionsObject
  32053. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  32054. * @apioption series.line.data
  32055. */
  32056. /**
  32057. * An additional, individual class name for the data point's graphic
  32058. * representation.
  32059. *
  32060. * @type {string}
  32061. * @since 5.0.0
  32062. * @product highcharts gantt
  32063. * @apioption series.line.data.className
  32064. */
  32065. /**
  32066. * Individual color for the point. By default the color is pulled from
  32067. * the global `colors` array.
  32068. *
  32069. * In styled mode, the `color` option doesn't take effect. Instead, use
  32070. * `colorIndex`.
  32071. *
  32072. * @sample {highcharts} highcharts/point/color/
  32073. * Mark the highest point
  32074. *
  32075. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  32076. * @product highcharts highstock gantt
  32077. * @apioption series.line.data.color
  32078. */
  32079. /**
  32080. * A specific color index to use for the point, so its graphic representations
  32081. * are given the class name `highcharts-color-{n}`. In styled mode this will
  32082. * change the color of the graphic. In non-styled mode, the color by is set by
  32083. * the `fill` attribute, so the change in class name won't have a visual effect
  32084. * by default.
  32085. *
  32086. * @type {number}
  32087. * @since 5.0.0
  32088. * @product highcharts gantt
  32089. * @apioption series.line.data.colorIndex
  32090. */
  32091. /**
  32092. * Individual data label for each point. The options are the same as
  32093. * the ones for [plotOptions.series.dataLabels](
  32094. * #plotOptions.series.dataLabels).
  32095. *
  32096. * @sample highcharts/point/datalabels/
  32097. * Show a label for the last value
  32098. *
  32099. * @declare Highcharts.DataLabelsOptionsObject
  32100. * @extends plotOptions.line.dataLabels
  32101. * @product highcharts highstock gantt
  32102. * @apioption series.line.data.dataLabels
  32103. */
  32104. /**
  32105. * A description of the point to add to the screen reader information
  32106. * about the point.
  32107. *
  32108. * @type {string}
  32109. * @since 5.0.0
  32110. * @requires modules/accessibility
  32111. * @apioption series.line.data.description
  32112. */
  32113. /**
  32114. * An id for the point. This can be used after render time to get a
  32115. * pointer to the point object through `chart.get()`.
  32116. *
  32117. * @sample {highcharts} highcharts/point/id/
  32118. * Remove an id'd point
  32119. *
  32120. * @type {string}
  32121. * @since 1.2.0
  32122. * @product highcharts highstock gantt
  32123. * @apioption series.line.data.id
  32124. */
  32125. /**
  32126. * The rank for this point's data label in case of collision. If two
  32127. * data labels are about to overlap, only the one with the highest `labelrank`
  32128. * will be drawn.
  32129. *
  32130. * @type {number}
  32131. * @apioption series.line.data.labelrank
  32132. */
  32133. /**
  32134. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  32135. *
  32136. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  32137. *
  32138. * @sample {highcharts} highcharts/series/data-array-of-objects/
  32139. * Point names
  32140. *
  32141. * @type {string}
  32142. * @apioption series.line.data.name
  32143. */
  32144. /**
  32145. * Whether the data point is selected initially.
  32146. *
  32147. * @type {boolean}
  32148. * @default false
  32149. * @product highcharts highstock gantt
  32150. * @apioption series.line.data.selected
  32151. */
  32152. /**
  32153. * The x value of the point. For datetime axes, the X value is the timestamp
  32154. * in milliseconds since 1970.
  32155. *
  32156. * @type {number}
  32157. * @product highcharts highstock
  32158. * @apioption series.line.data.x
  32159. */
  32160. /**
  32161. * The y value of the point.
  32162. *
  32163. * @type {number|null}
  32164. * @product highcharts highstock
  32165. * @apioption series.line.data.y
  32166. */
  32167. /**
  32168. * The individual point events.
  32169. *
  32170. * @extends plotOptions.series.point.events
  32171. * @product highcharts highstock gantt
  32172. * @apioption series.line.data.events
  32173. */
  32174. /**
  32175. * Options for the point markers of line-like series.
  32176. *
  32177. * @declare Highcharts.PointMarkerOptionsObject
  32178. * @extends plotOptions.series.marker
  32179. * @product highcharts highstock
  32180. * @apioption series.line.data.marker
  32181. */
  32182. ''; // include precedent doclets in transpilat
  32183. });
  32184. _registerModule(_modules, 'parts/Stacking.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32185. /* *
  32186. *
  32187. * (c) 2010-2019 Torstein Honsi
  32188. *
  32189. * License: www.highcharts.com/license
  32190. *
  32191. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  32192. *
  32193. * */
  32194. /**
  32195. * Stack of data points
  32196. *
  32197. * @product highcharts
  32198. *
  32199. * @interface Highcharts.StackItemObject
  32200. */ /**
  32201. * Alignment settings
  32202. * @name Highcharts.StackItemObject#alignOptions
  32203. * @type {Highcharts.AlignObject}
  32204. */ /**
  32205. * Related axis
  32206. * @name Highcharts.StackItemObject#axis
  32207. * @type {Highcharts.Axis}
  32208. */ /**
  32209. * Cumulative value of the stacked data points
  32210. * @name Highcharts.StackItemObject#cumulative
  32211. * @type {number}
  32212. */ /**
  32213. * True if on the negative side
  32214. * @name Highcharts.StackItemObject#isNegative
  32215. * @type {boolean}
  32216. */ /**
  32217. * Related SVG element
  32218. * @name Highcharts.StackItemObject#label
  32219. * @type {Highcharts.SVGElement}
  32220. */ /**
  32221. * Related stack options
  32222. * @name Highcharts.StackItemObject#options
  32223. * @type {Highcharts.YAxisStackLabelsOptions}
  32224. */ /**
  32225. * Total value of the stacked data points
  32226. * @name Highcharts.StackItemObject#total
  32227. * @type {number}
  32228. */ /**
  32229. * Shared x value of the stack
  32230. * @name Highcharts.StackItemObject#x
  32231. * @type {number}
  32232. */
  32233. var correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, objectEach = U.objectEach, pick = U.pick;
  32234. var Axis = H.Axis, Chart = H.Chart, format = H.format, Series = H.Series;
  32235. /* eslint-disable no-invalid-this, valid-jsdoc */
  32236. /**
  32237. * The class for stacks. Each stack, on a specific X value and either negative
  32238. * or positive, has its own stack item.
  32239. *
  32240. * @private
  32241. * @class
  32242. * @name Highcharts.StackItem
  32243. * @param {Highcharts.Axis} axis
  32244. * @param {Highcharts.YAxisStackLabelsOptions} options
  32245. * @param {boolean} isNegative
  32246. * @param {number} x
  32247. * @param {Highcharts.OptionsStackingValue} [stackOption]
  32248. */
  32249. H.StackItem = function (axis, options, isNegative, x, stackOption) {
  32250. var inverted = axis.chart.inverted;
  32251. this.axis = axis;
  32252. // Tells if the stack is negative
  32253. this.isNegative = isNegative;
  32254. // Save the options to be able to style the label
  32255. this.options = options = options || {};
  32256. // Save the x value to be able to position the label later
  32257. this.x = x;
  32258. // Initialize total value
  32259. this.total = null;
  32260. // This will keep each points' extremes stored by series.index and point
  32261. // index
  32262. this.points = {};
  32263. // Save the stack option on the series configuration object, and whether to
  32264. // treat it as percent
  32265. this.stack = stackOption;
  32266. this.leftCliff = 0;
  32267. this.rightCliff = 0;
  32268. // The align options and text align varies on whether the stack is negative
  32269. // and if the chart is inverted or not.
  32270. // First test the user supplied value, then use the dynamic.
  32271. this.alignOptions = {
  32272. align: options.align ||
  32273. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  32274. verticalAlign: options.verticalAlign ||
  32275. (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
  32276. y: options.y,
  32277. x: options.x
  32278. };
  32279. this.textAlign = options.textAlign ||
  32280. (inverted ? (isNegative ? 'right' : 'left') : 'center');
  32281. };
  32282. H.StackItem.prototype = {
  32283. /**
  32284. * @private
  32285. * @function Highcharts.StackItem#destroy
  32286. * @return {void}
  32287. */
  32288. destroy: function () {
  32289. destroyObjectProperties(this, this.axis);
  32290. },
  32291. /**
  32292. * Renders the stack total label and adds it to the stack label group.
  32293. *
  32294. * @private
  32295. * @function Highcharts.StackItem#render
  32296. * @param {Highcharts.SVGElement} group
  32297. * @return {void}
  32298. */
  32299. render: function (group) {
  32300. var chart = this.axis.chart, options = this.options, formatOption = options.format, attr = {}, str = formatOption ? // format the text in the label
  32301. format(formatOption, this, chart) :
  32302. options.formatter.call(this);
  32303. // Change the text to reflect the new total and set visibility to hidden
  32304. // in case the serie is hidden
  32305. if (this.label) {
  32306. this.label.attr({ text: str, visibility: 'hidden' });
  32307. }
  32308. else {
  32309. // Create new label
  32310. this.label = chart.renderer
  32311. .label(str, null, null, options.shape, null, null, options.useHTML, false, 'stack-labels');
  32312. attr = {
  32313. text: str,
  32314. align: this.textAlign,
  32315. rotation: options.rotation,
  32316. padding: pick(options.padding, 0),
  32317. visibility: 'hidden' // hidden until setOffset is called
  32318. };
  32319. this.label.attr(attr);
  32320. if (!chart.styledMode) {
  32321. this.label.css(options.style);
  32322. }
  32323. if (!this.label.added) {
  32324. this.label.add(group); // add to the labels-group
  32325. }
  32326. }
  32327. // Rank it higher than data labels (#8742)
  32328. this.label.labelrank = chart.plotHeight;
  32329. },
  32330. /**
  32331. * Sets the offset that the stack has from the x value and repositions the
  32332. * label.
  32333. *
  32334. * @private
  32335. * @function Highcarts.StackItem#setOffset
  32336. * @param {number} xOffset
  32337. * @param {number} xWidth
  32338. * @param {number} [boxBottom]
  32339. * @param {number} [boxTop]
  32340. * @param {number} [defaultX]
  32341. * @return {void}
  32342. */
  32343. setOffset: function (xOffset, xWidth, boxBottom, boxTop, defaultX) {
  32344. var stackItem = this, axis = stackItem.axis, chart = axis.chart,
  32345. // stack value translated mapped to chart coordinates
  32346. y = axis.translate(axis.usePercentage ?
  32347. 100 :
  32348. (boxTop ?
  32349. boxTop :
  32350. stackItem.total), 0, 0, 0, 1), yZero = axis.translate(boxBottom ? boxBottom : 0), // stack origin
  32351. // stack height:
  32352. h = defined(y) && Math.abs(y - yZero),
  32353. // x position:
  32354. x = pick(defaultX, chart.xAxis[0].translate(stackItem.x)) +
  32355. 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', visible, alignAttr;
  32356. if (label && stackBox) {
  32357. var bBox = label.getBBox(), boxOffsetX = chart.inverted ?
  32358. (isNegative ? bBox.width : 0) : bBox.width / 2, boxOffsetY = chart.inverted ?
  32359. bBox.height / 2 : (isNegative ? -4 : bBox.height + 4);
  32360. stackItem.alignOptions.x = pick(stackItem.options.x, 0);
  32361. // Align the label to the box
  32362. label.align(stackItem.alignOptions, null, stackBox);
  32363. // Set visibility (#678)
  32364. alignAttr = label.alignAttr;
  32365. label.show();
  32366. // Set label above/under stackBox
  32367. alignAttr.y -= boxOffsetY;
  32368. if (isJustify) {
  32369. // Set label x position for justifyDataLabel function
  32370. alignAttr.x -= boxOffsetX;
  32371. Series.prototype.justifyDataLabel.call(this.axis, label, stackItem.alignOptions, alignAttr, bBox, stackBox);
  32372. alignAttr.x += boxOffsetX;
  32373. }
  32374. alignAttr.x = label.alignAttr.x;
  32375. label.attr({
  32376. x: alignAttr.x,
  32377. y: alignAttr.y
  32378. });
  32379. if (pick(!isJustify && stackItem.options.crop, true)) {
  32380. visible = chart.isInsidePlot(label.x +
  32381. (chart.inverted ? 0 : -bBox.width / 2), label.y) &&
  32382. chart.isInsidePlot(label.x + (chart.inverted ?
  32383. (isNegative ? -bBox.width : bBox.width) :
  32384. bBox.width / 2), label.y + bBox.height);
  32385. if (!visible) {
  32386. label.hide();
  32387. }
  32388. }
  32389. }
  32390. },
  32391. /**
  32392. * @private
  32393. * @function Highcharts.StackItem#getStackBox
  32394. *
  32395. * @param {Highcharts.Chart} chart
  32396. *
  32397. * @param {Highcharts.StackItem} stackItem
  32398. *
  32399. * @param {number} x
  32400. *
  32401. * @param {number} y
  32402. *
  32403. * @param {number} xWidth
  32404. *
  32405. * @param {number} h
  32406. *
  32407. * @param {Highcharts.Axis} axis
  32408. *
  32409. * @return {Highcharts.BBoxObject}
  32410. */
  32411. getStackBox: function (chart, stackItem, x, y, xWidth, h, axis) {
  32412. var reversed = stackItem.axis.reversed, inverted = chart.inverted, axisPos = axis.height + axis.pos -
  32413. (inverted ? chart.plotLeft : chart.plotTop), neg = (stackItem.isNegative && !reversed) ||
  32414. (!stackItem.isNegative && reversed); // #4056
  32415. return {
  32416. x: inverted ? (neg ? y : y - h) : x,
  32417. y: inverted ?
  32418. axisPos - x - xWidth :
  32419. (neg ?
  32420. (axisPos - y - h) :
  32421. axisPos - y),
  32422. width: inverted ? h : xWidth,
  32423. height: inverted ? xWidth : h
  32424. };
  32425. }
  32426. };
  32427. /**
  32428. * Generate stacks for each series and calculate stacks total values
  32429. *
  32430. * @private
  32431. * @function Highcharts.Chart#getStacks
  32432. * @return {void}
  32433. */
  32434. Chart.prototype.getStacks = function () {
  32435. var chart = this, inverted = chart.inverted;
  32436. // reset stacks for each yAxis
  32437. chart.yAxis.forEach(function (axis) {
  32438. if (axis.stacks && axis.hasVisibleSeries) {
  32439. axis.oldStacks = axis.stacks;
  32440. }
  32441. });
  32442. chart.series.forEach(function (series) {
  32443. var xAxisOptions = series.xAxis && series.xAxis.options || {};
  32444. if (series.options.stacking &&
  32445. (series.visible === true ||
  32446. chart.options.chart.ignoreHiddenSeries === false)) {
  32447. series.stackKey = [
  32448. series.type,
  32449. pick(series.options.stack, ''),
  32450. inverted ? xAxisOptions.top : xAxisOptions.left,
  32451. inverted ? xAxisOptions.height : xAxisOptions.width
  32452. ].join(',');
  32453. }
  32454. });
  32455. };
  32456. // Stacking methods defined on the Axis prototype
  32457. /**
  32458. * Build the stacks from top down
  32459. *
  32460. * @private
  32461. * @function Highcharts.Axis#buildStacks
  32462. * @return {void}
  32463. */
  32464. Axis.prototype.buildStacks = function () {
  32465. var axisSeries = this.series, reversedStacks = pick(this.options.reversedStacks, true), len = axisSeries.length, actualSeries, i;
  32466. if (!this.isXAxis) {
  32467. this.usePercentage = false;
  32468. i = len;
  32469. while (i--) {
  32470. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  32471. actualSeries.setStackedPoints();
  32472. }
  32473. // Loop up again to compute percent and stream stack
  32474. for (i = 0; i < len; i++) {
  32475. axisSeries[i].modifyStacks();
  32476. }
  32477. H.fireEvent(this, 'afterBuildStacks');
  32478. }
  32479. };
  32480. /**
  32481. * @private
  32482. * @function Highcharts.Axis#renderStackTotals
  32483. * @return {vopid}
  32484. */
  32485. Axis.prototype.renderStackTotals = function () {
  32486. var axis = this, chart = axis.chart, renderer = chart.renderer, stacks = axis.stacks, stackTotalGroup = axis.stackTotalGroup;
  32487. // Create a separate group for the stack total labels
  32488. if (!stackTotalGroup) {
  32489. axis.stackTotalGroup = stackTotalGroup =
  32490. renderer
  32491. .g('stack-labels')
  32492. .attr({
  32493. visibility: 'visible',
  32494. zIndex: 6
  32495. })
  32496. .add();
  32497. }
  32498. // plotLeft/Top will change when y axis gets wider so we need to translate
  32499. // the stackTotalGroup at every render call. See bug #506 and #516
  32500. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  32501. // Render each stack total
  32502. objectEach(stacks, function (type) {
  32503. objectEach(type, function (stack) {
  32504. stack.render(stackTotalGroup);
  32505. });
  32506. });
  32507. };
  32508. /**
  32509. * Set all the stacks to initial states and destroy unused ones.
  32510. *
  32511. * @private
  32512. * @function Highcharts.Axis#resetStacks
  32513. * @return {void}
  32514. */
  32515. Axis.prototype.resetStacks = function () {
  32516. var axis = this, stacks = axis.stacks;
  32517. if (!axis.isXAxis) {
  32518. objectEach(stacks, function (type) {
  32519. objectEach(type, function (stack, key) {
  32520. // Clean up memory after point deletion (#1044, #4320)
  32521. if (stack.touched < axis.stacksTouched) {
  32522. stack.destroy();
  32523. delete type[key];
  32524. // Reset stacks
  32525. }
  32526. else {
  32527. stack.total = null;
  32528. stack.cumulative = null;
  32529. }
  32530. });
  32531. });
  32532. }
  32533. };
  32534. /**
  32535. * @private
  32536. * @function Highcharts.Axis#cleanStacks
  32537. * @return {void}
  32538. */
  32539. Axis.prototype.cleanStacks = function () {
  32540. var stacks;
  32541. if (!this.isXAxis) {
  32542. if (this.oldStacks) {
  32543. stacks = this.stacks = this.oldStacks;
  32544. }
  32545. // reset stacks
  32546. objectEach(stacks, function (type) {
  32547. objectEach(type, function (stack) {
  32548. stack.cumulative = stack.total;
  32549. });
  32550. });
  32551. }
  32552. };
  32553. // Stacking methods defnied for Series prototype
  32554. /**
  32555. * Adds series' points value to corresponding stack
  32556. *
  32557. * @private
  32558. * @function Highcharts.Series#setStackedPoints
  32559. * @return {void}
  32560. */
  32561. Series.prototype.setStackedPoints = function () {
  32562. if (!this.options.stacking ||
  32563. (this.visible !== true &&
  32564. this.chart.options.chart.ignoreHiddenSeries !== false)) {
  32565. return;
  32566. }
  32567. 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, stacking = seriesOptions.stacking, stackKey = series.stackKey, negKey = '-' + stackKey, negStacks = series.negStacks, yAxis = series.yAxis, stacks = yAxis.stacks, oldStacks = yAxis.oldStacks, stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  32568. yAxis.stacksTouched += 1;
  32569. // loop over the non-null y values and read them into a local array
  32570. for (i = 0; i < yDataLength; i++) {
  32571. x = xData[i];
  32572. y = yData[i];
  32573. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  32574. pointKey = stackIndicator.key;
  32575. // Read stacked values into a stack based on the x value,
  32576. // the sign of y and the stack key. Stacking is also handled for null
  32577. // values (#739)
  32578. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  32579. key = isNegative ? negKey : stackKey;
  32580. // Create empty object for this stack if it doesn't exist yet
  32581. if (!stacks[key]) {
  32582. stacks[key] =
  32583. {};
  32584. }
  32585. // Initialize StackItem for this x
  32586. if (!stacks[key][x]) {
  32587. if (oldStacks[key] &&
  32588. oldStacks[key][x]) {
  32589. stacks[key][x] = oldStacks[key][x];
  32590. stacks[key][x].total = null;
  32591. }
  32592. else {
  32593. stacks[key][x] = new H.StackItem(yAxis, yAxis.options.stackLabels, isNegative, x, stackOption);
  32594. }
  32595. }
  32596. // If the StackItem doesn't exist, create it first
  32597. stack = stacks[key][x];
  32598. if (y !== null) {
  32599. stack.points[pointKey] = stack.points[series.index] =
  32600. [pick(stack.cumulative, stackThreshold)];
  32601. // Record the base of the stack
  32602. if (!defined(stack.cumulative)) {
  32603. stack.base = pointKey;
  32604. }
  32605. stack.touched = yAxis.stacksTouched;
  32606. // In area charts, if there are multiple points on the same X value,
  32607. // let the area fill the full span of those points
  32608. if (stackIndicator.index > 0 && series.singleStacks === false) {
  32609. stack.points[pointKey][0] =
  32610. stack.points[series.index + ',' + x + ',0'][0];
  32611. }
  32612. // When updating to null, reset the point stack (#7493)
  32613. }
  32614. else {
  32615. stack.points[pointKey] = stack.points[series.index] =
  32616. null;
  32617. }
  32618. // Add value to the stack total
  32619. if (stacking === 'percent') {
  32620. // Percent stacked column, totals are the same for the positive and
  32621. // negative stacks
  32622. other = isNegative ? stackKey : negKey;
  32623. if (negStacks && stacks[other] && stacks[other][x]) {
  32624. other = stacks[other][x];
  32625. stack.total = other.total =
  32626. Math.max(other.total, stack.total) +
  32627. Math.abs(y) ||
  32628. 0;
  32629. // Percent stacked areas
  32630. }
  32631. else {
  32632. stack.total =
  32633. correctFloat(stack.total + (Math.abs(y) || 0));
  32634. }
  32635. }
  32636. else {
  32637. stack.total = correctFloat(stack.total + (y || 0));
  32638. }
  32639. stack.cumulative =
  32640. pick(stack.cumulative, stackThreshold) + (y || 0);
  32641. if (y !== null) {
  32642. stack.points[pointKey].push(stack.cumulative);
  32643. stackedYData[i] = stack.cumulative;
  32644. }
  32645. }
  32646. if (stacking === 'percent') {
  32647. yAxis.usePercentage = true;
  32648. }
  32649. this.stackedYData = stackedYData; // To be used in getExtremes
  32650. // Reset old stacks
  32651. yAxis.oldStacks = {};
  32652. };
  32653. /**
  32654. * Iterate over all stacks and compute the absolute values to percent
  32655. *
  32656. * @private
  32657. * @function Highcharts.Series#modifyStacks
  32658. * @return {void}
  32659. */
  32660. Series.prototype.modifyStacks = function () {
  32661. var series = this, stackKey = series.stackKey, stacks = series.yAxis.stacks, processedXData = series.processedXData, stackIndicator, stacking = series.options.stacking;
  32662. if (series[stacking + 'Stacker']) { // Modifier function exists
  32663. [stackKey, '-' + stackKey].forEach(function (key) {
  32664. var i = processedXData.length, x, stack, pointExtremes;
  32665. while (i--) {
  32666. x = processedXData[i];
  32667. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  32668. stack = stacks[key] && stacks[key][x];
  32669. pointExtremes =
  32670. stack && stack.points[stackIndicator.key];
  32671. if (pointExtremes) {
  32672. series[stacking + 'Stacker'](pointExtremes, stack, i);
  32673. }
  32674. }
  32675. });
  32676. }
  32677. };
  32678. /**
  32679. * Modifier function for percent stacks. Blows up the stack to 100%.
  32680. *
  32681. * @private
  32682. * @function Highcharts.Series#percentStacker
  32683. * @param {Array<number>} pointExtremes
  32684. * @param {Highcharts.StackItem} stack
  32685. * @param {number} i
  32686. * @return {void}
  32687. */
  32688. Series.prototype.percentStacker = function (pointExtremes, stack, i) {
  32689. var totalFactor = stack.total ? 100 / stack.total : 0;
  32690. // Y bottom value
  32691. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  32692. // Y value
  32693. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  32694. this.stackedYData[i] = pointExtremes[1];
  32695. };
  32696. /**
  32697. * Get stack indicator, according to it's x-value, to determine points with the
  32698. * same x-value
  32699. *
  32700. * @private
  32701. * @function Highcharts.Series#getStackIndicator
  32702. * @param {Highcharts.StackItemIndicatorObject|undefined} stackIndicator
  32703. * @param {number} x
  32704. * @param {number} index
  32705. * @param {string} [key]
  32706. * @return {Highcharts.StackItemIndicatorObject}
  32707. */
  32708. Series.prototype.getStackIndicator = function (stackIndicator, x, index, key) {
  32709. // Update stack indicator, when:
  32710. // first point in a stack || x changed || stack type (negative vs positive)
  32711. // changed:
  32712. if (!defined(stackIndicator) ||
  32713. stackIndicator.x !== x ||
  32714. (key && stackIndicator.key !== key)) {
  32715. stackIndicator = {
  32716. x: x,
  32717. index: 0,
  32718. key: key
  32719. };
  32720. }
  32721. else {
  32722. stackIndicator.index++;
  32723. }
  32724. stackIndicator.key =
  32725. [index, x, stackIndicator.index].join(',');
  32726. return stackIndicator;
  32727. };
  32728. });
  32729. _registerModule(_modules, 'parts/Dynamics.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32730. /* *
  32731. *
  32732. * (c) 2010-2019 Torstein Honsi
  32733. *
  32734. * License: www.highcharts.com/license
  32735. *
  32736. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  32737. *
  32738. * */
  32739. var defined = U.defined, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation, splat = U.splat;
  32740. var addEvent = H.addEvent, animate = H.animate, Axis = H.Axis, Chart = H.Chart, createElement = H.createElement, css = H.css, fireEvent = H.fireEvent, merge = H.merge, Point = H.Point, Series = H.Series, seriesTypes = H.seriesTypes;
  32741. /* eslint-disable valid-jsdoc */
  32742. /**
  32743. * Remove settings that have not changed, to avoid unnecessary rendering or
  32744. * computing (#9197).
  32745. * @private
  32746. */
  32747. H.cleanRecursively = function (newer, older) {
  32748. var result = {};
  32749. objectEach(newer, function (val, key) {
  32750. var ob;
  32751. // Dive into objects (except DOM nodes)
  32752. if (isObject(newer[key], true) &&
  32753. !newer.nodeType && // #10044
  32754. older[key]) {
  32755. ob = H.cleanRecursively(newer[key], older[key]);
  32756. if (Object.keys(ob).length) {
  32757. result[key] = ob;
  32758. }
  32759. // Arrays, primitives and DOM nodes are copied directly
  32760. }
  32761. else if (isObject(newer[key]) ||
  32762. newer[key] !== older[key]) {
  32763. result[key] = newer[key];
  32764. }
  32765. });
  32766. return result;
  32767. };
  32768. // Extend the Chart prototype for dynamic methods
  32769. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  32770. /**
  32771. * Add a series to the chart after render time. Note that this method should
  32772. * never be used when adding data synchronously at chart render time, as it
  32773. * adds expense to the calculations and rendering. When adding data at the
  32774. * same time as the chart is initialized, add the series as a configuration
  32775. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  32776. *
  32777. * @sample highcharts/members/chart-addseries/
  32778. * Add a series from a button
  32779. * @sample stock/members/chart-addseries/
  32780. * Add a series in Highstock
  32781. *
  32782. * @function Highcharts.Chart#addSeries
  32783. *
  32784. * @param {Highcharts.SeriesOptionsType} options
  32785. * The config options for the series.
  32786. *
  32787. * @param {boolean} [redraw=true]
  32788. * Whether to redraw the chart after adding.
  32789. *
  32790. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  32791. * Whether to apply animation, and optionally animation
  32792. * configuration.
  32793. *
  32794. * @return {Highcharts.Series}
  32795. * The newly created series object.
  32796. *
  32797. * @fires Highcharts.Chart#event:addSeries
  32798. * @fires Highcharts.Chart#event:afterAddSeries
  32799. */
  32800. addSeries: function (options, redraw, animation) {
  32801. var series, chart = this;
  32802. if (options) { // <- not necessary
  32803. redraw = pick(redraw, true); // defaults to true
  32804. fireEvent(chart, 'addSeries', { options: options }, function () {
  32805. series = chart.initSeries(options);
  32806. chart.isDirtyLegend = true;
  32807. chart.linkSeries();
  32808. if (series.enabledDataSorting) {
  32809. // We need to call `setData` after `linkSeries`
  32810. series.setData(options.data, false);
  32811. }
  32812. fireEvent(chart, 'afterAddSeries', { series: series });
  32813. if (redraw) {
  32814. chart.redraw(animation);
  32815. }
  32816. });
  32817. }
  32818. return series;
  32819. },
  32820. /**
  32821. * Add an axis to the chart after render time. Note that this method should
  32822. * never be used when adding data synchronously at chart render time, as it
  32823. * adds expense to the calculations and rendering. When adding data at the
  32824. * same time as the chart is initialized, add the axis as a configuration
  32825. * option instead.
  32826. *
  32827. * @sample highcharts/members/chart-addaxis/
  32828. * Add and remove axes
  32829. *
  32830. * @function Highcharts.Chart#addAxis
  32831. *
  32832. * @param {Highcharts.AxisOptions} options
  32833. * The axis options.
  32834. *
  32835. * @param {boolean} [isX=false]
  32836. * Whether it is an X axis or a value axis.
  32837. *
  32838. * @param {boolean} [redraw=true]
  32839. * Whether to redraw the chart after adding.
  32840. *
  32841. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  32842. * Whether and how to apply animation in the redraw.
  32843. *
  32844. * @return {Highcharts.Axis}
  32845. * The newly generated Axis object.
  32846. */
  32847. addAxis: function (options, isX, redraw, animation) {
  32848. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  32849. },
  32850. /**
  32851. * Add a color axis to the chart after render time. Note that this method
  32852. * should never be used when adding data synchronously at chart render time,
  32853. * as it adds expense to the calculations and rendering. When adding data at
  32854. * the same time as the chart is initialized, add the axis as a
  32855. * configuration option instead.
  32856. *
  32857. * @sample highcharts/members/chart-addaxis/
  32858. * Add and remove axes
  32859. *
  32860. * @function Highcharts.Chart#addColorAxis
  32861. *
  32862. * @param {Highcharts.ColorAxisOptions} options
  32863. * The axis options.
  32864. *
  32865. * @param {boolean} [redraw=true]
  32866. * Whether to redraw the chart after adding.
  32867. *
  32868. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  32869. * Whether and how to apply animation in the redraw.
  32870. *
  32871. * @return {Highcharts.ColorAxis}
  32872. * The newly generated Axis object.
  32873. */
  32874. addColorAxis: function (options, redraw, animation) {
  32875. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  32876. },
  32877. /**
  32878. * Factory for creating different axis types.
  32879. *
  32880. * @private
  32881. * @function Highcharts.Chart#createAxis
  32882. *
  32883. * @param {string} type
  32884. * An axis type.
  32885. *
  32886. * @param {...Array<*>} arguments
  32887. * All arguments for the constructor.
  32888. *
  32889. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  32890. * The newly generated Axis object.
  32891. */
  32892. createAxis: function (type, options) {
  32893. var chartOptions = this.options, isColorAxis = type === 'colorAxis', axisOptions = options.axis, redraw = options.redraw, animation = options.animation, userOptions = merge(axisOptions, {
  32894. index: this[type].length,
  32895. isX: type === 'xAxis'
  32896. }), axis;
  32897. if (isColorAxis) {
  32898. axis = new H.ColorAxis(this, userOptions);
  32899. }
  32900. else {
  32901. axis = new Axis(this, userOptions);
  32902. }
  32903. // Push the new axis options to the chart options
  32904. chartOptions[type] = splat(chartOptions[type] || {});
  32905. chartOptions[type].push(userOptions);
  32906. if (isColorAxis) {
  32907. this.isDirtyLegend = true;
  32908. // Clear before 'bindAxes' (#11924)
  32909. this.axes.forEach(function (axis) {
  32910. axis.series = [];
  32911. });
  32912. this.series.forEach(function (series) {
  32913. series.bindAxes();
  32914. series.isDirtyData = true;
  32915. });
  32916. }
  32917. if (pick(redraw, true)) {
  32918. this.redraw(animation);
  32919. }
  32920. return axis;
  32921. },
  32922. /**
  32923. * Dim the chart and show a loading text or symbol. Options for the loading
  32924. * screen are defined in {@link
  32925. * https://api.highcharts.com/highcharts/loading|the loading options}.
  32926. *
  32927. * @sample highcharts/members/chart-hideloading/
  32928. * Show and hide loading from a button
  32929. * @sample highcharts/members/chart-showloading/
  32930. * Apply different text labels
  32931. * @sample stock/members/chart-show-hide-loading/
  32932. * Toggle loading in Highstock
  32933. *
  32934. * @function Highcharts.Chart#showLoading
  32935. *
  32936. * @param {string} [str]
  32937. * An optional text to show in the loading label instead of the
  32938. * default one. The default text is set in
  32939. * [lang.loading](http://api.highcharts.com/highcharts/lang.loading).
  32940. *
  32941. * @return {void}
  32942. */
  32943. showLoading: function (str) {
  32944. var chart = this, options = chart.options, loadingDiv = chart.loadingDiv, loadingOptions = options.loading, setLoadingSize = function () {
  32945. if (loadingDiv) {
  32946. css(loadingDiv, {
  32947. left: chart.plotLeft + 'px',
  32948. top: chart.plotTop + 'px',
  32949. width: chart.plotWidth + 'px',
  32950. height: chart.plotHeight + 'px'
  32951. });
  32952. }
  32953. };
  32954. // create the layer at the first call
  32955. if (!loadingDiv) {
  32956. chart.loadingDiv = loadingDiv = createElement('div', {
  32957. className: 'highcharts-loading highcharts-loading-hidden'
  32958. }, null, chart.container);
  32959. chart.loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  32960. addEvent(chart, 'redraw', setLoadingSize); // #1080
  32961. }
  32962. loadingDiv.className = 'highcharts-loading';
  32963. // Update text
  32964. chart.loadingSpan.innerHTML =
  32965. pick(str, options.lang.loading, '');
  32966. if (!chart.styledMode) {
  32967. // Update visuals
  32968. css(loadingDiv, extend(loadingOptions.style, {
  32969. zIndex: 10
  32970. }));
  32971. css(chart.loadingSpan, loadingOptions.labelStyle);
  32972. // Show it
  32973. if (!chart.loadingShown) {
  32974. css(loadingDiv, {
  32975. opacity: 0,
  32976. display: ''
  32977. });
  32978. animate(loadingDiv, {
  32979. opacity: loadingOptions.style.opacity || 0.5
  32980. }, {
  32981. duration: loadingOptions.showDuration || 0
  32982. });
  32983. }
  32984. }
  32985. chart.loadingShown = true;
  32986. setLoadingSize();
  32987. },
  32988. /**
  32989. * Hide the loading layer.
  32990. *
  32991. * @see Highcharts.Chart#showLoading
  32992. *
  32993. * @sample highcharts/members/chart-hideloading/
  32994. * Show and hide loading from a button
  32995. * @sample stock/members/chart-show-hide-loading/
  32996. * Toggle loading in Highstock
  32997. *
  32998. * @function Highcharts.Chart#hideLoading
  32999. *
  33000. * @return {void}
  33001. */
  33002. hideLoading: function () {
  33003. var options = this.options, loadingDiv = this.loadingDiv;
  33004. if (loadingDiv) {
  33005. loadingDiv.className =
  33006. 'highcharts-loading highcharts-loading-hidden';
  33007. if (!this.styledMode) {
  33008. animate(loadingDiv, {
  33009. opacity: 0
  33010. }, {
  33011. duration: options.loading.hideDuration || 100,
  33012. complete: function () {
  33013. css(loadingDiv, { display: 'none' });
  33014. }
  33015. });
  33016. }
  33017. }
  33018. this.loadingShown = false;
  33019. },
  33020. /**
  33021. * These properties cause isDirtyBox to be set to true when updating. Can be
  33022. * extended from plugins.
  33023. */
  33024. propsRequireDirtyBox: [
  33025. 'backgroundColor',
  33026. 'borderColor',
  33027. 'borderWidth',
  33028. 'borderRadius',
  33029. 'plotBackgroundColor',
  33030. 'plotBackgroundImage',
  33031. 'plotBorderColor',
  33032. 'plotBorderWidth',
  33033. 'plotShadow',
  33034. 'shadow'
  33035. ],
  33036. /**
  33037. * These properties require a full reflow of chart elements, best
  33038. * implemented through running `Chart.setSize` internally (#8190).
  33039. * @type {Array}
  33040. */
  33041. propsRequireReflow: [
  33042. 'margin',
  33043. 'marginTop',
  33044. 'marginRight',
  33045. 'marginBottom',
  33046. 'marginLeft',
  33047. 'spacing',
  33048. 'spacingTop',
  33049. 'spacingRight',
  33050. 'spacingBottom',
  33051. 'spacingLeft'
  33052. ],
  33053. /**
  33054. * These properties cause all series to be updated when updating. Can be
  33055. * extended from plugins.
  33056. */
  33057. propsRequireUpdateSeries: [
  33058. 'chart.inverted',
  33059. 'chart.polar',
  33060. 'chart.ignoreHiddenSeries',
  33061. 'chart.type',
  33062. 'colors',
  33063. 'plotOptions',
  33064. 'time',
  33065. 'tooltip'
  33066. ],
  33067. /**
  33068. * These collections (arrays) implement update() methods with support for
  33069. * one-to-one option.
  33070. */
  33071. collectionsWithUpdate: [
  33072. 'xAxis',
  33073. 'yAxis',
  33074. 'zAxis',
  33075. 'series'
  33076. ],
  33077. /**
  33078. * A generic function to update any element of the chart. Elements can be
  33079. * enabled and disabled, moved, re-styled, re-formatted etc.
  33080. *
  33081. * A special case is configuration objects that take arrays, for example
  33082. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  33083. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  33084. * [series](https://api.highcharts.com/highcharts/series). For these
  33085. * collections, an `id` option is used to map the new option set to an
  33086. * existing object. If an existing object of the same id is not found, the
  33087. * corresponding item is updated. So for example, running `chart.update`
  33088. * with a series item without an id, will cause the existing chart's series
  33089. * with the same index in the series array to be updated. When the
  33090. * `oneToOne` parameter is true, `chart.update` will also take care of
  33091. * adding and removing items from the collection. Read more under the
  33092. * parameter description below.
  33093. *
  33094. * Note that when changing series data, `chart.update` may mutate the passed
  33095. * data options.
  33096. *
  33097. * See also the
  33098. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  33099. * Switching between `responsive.rules` basically runs `chart.update` under
  33100. * the hood.
  33101. *
  33102. * @sample highcharts/members/chart-update/
  33103. * Update chart geometry
  33104. *
  33105. * @function Highcharts.Chart#update
  33106. *
  33107. * @param {Highcharts.Options} options
  33108. * A configuration object for the new chart options.
  33109. *
  33110. * @param {boolean} [redraw=true]
  33111. * Whether to redraw the chart.
  33112. *
  33113. * @param {boolean} [oneToOne=false]
  33114. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  33115. * collections will be updated one to one, and items will be either
  33116. * added or removed to match the new updated options. For example,
  33117. * if the chart has two series and we call `chart.update` with a
  33118. * configuration containing three series, one will be added. If we
  33119. * call `chart.update` with one series, one will be removed. Setting
  33120. * an empty `series` array will remove all series, but leaving out
  33121. * the`series` property will leave all series untouched. If the
  33122. * series have id's, the new series options will be matched by id,
  33123. * and the remaining ones removed.
  33124. *
  33125. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  33126. * Whether to apply animation, and optionally animation
  33127. * configuration.
  33128. *
  33129. * @return {void}
  33130. *
  33131. * @fires Highcharts.Chart#event:update
  33132. * @fires Highcharts.Chart#event:afterUpdate
  33133. */
  33134. update: function (options, redraw, oneToOne, animation) {
  33135. var chart = this, adders = {
  33136. credits: 'addCredits',
  33137. title: 'setTitle',
  33138. subtitle: 'setSubtitle',
  33139. caption: 'setCaption'
  33140. }, optionsChart, updateAllAxes, updateAllSeries, newWidth, newHeight, runSetSize, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  33141. fireEvent(chart, 'update', { options: options });
  33142. // If there are responsive rules in action, undo the responsive rules
  33143. // before we apply the updated options and replay the responsive rules
  33144. // on top from the chart.redraw function (#9617).
  33145. if (!isResponsiveOptions) {
  33146. chart.setResponsive(false, true);
  33147. }
  33148. options = H.cleanRecursively(options, chart.options);
  33149. merge(true, chart.userOptions, options);
  33150. // If the top-level chart option is present, some special updates are
  33151. // required
  33152. optionsChart = options.chart;
  33153. if (optionsChart) {
  33154. merge(true, chart.options.chart, optionsChart);
  33155. // Setter function
  33156. if ('className' in optionsChart) {
  33157. chart.setClassName(optionsChart.className);
  33158. }
  33159. if ('reflow' in optionsChart) {
  33160. chart.setReflow(optionsChart.reflow);
  33161. }
  33162. if ('inverted' in optionsChart ||
  33163. 'polar' in optionsChart ||
  33164. 'type' in optionsChart) {
  33165. // Parse options.chart.inverted and options.chart.polar together
  33166. // with the available series.
  33167. chart.propFromSeries();
  33168. updateAllAxes = true;
  33169. }
  33170. if ('alignTicks' in optionsChart) { // #6452
  33171. updateAllAxes = true;
  33172. }
  33173. objectEach(optionsChart, function (val, key) {
  33174. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  33175. -1) {
  33176. updateAllSeries = true;
  33177. }
  33178. // Only dirty box
  33179. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  33180. chart.isDirtyBox = true;
  33181. }
  33182. // Chart setSize
  33183. if (!isResponsiveOptions &&
  33184. chart.propsRequireReflow.indexOf(key) !== -1) {
  33185. runSetSize = true;
  33186. }
  33187. });
  33188. if (!chart.styledMode && 'style' in optionsChart) {
  33189. chart.renderer.setStyle(optionsChart.style);
  33190. }
  33191. }
  33192. // Moved up, because tooltip needs updated plotOptions (#6218)
  33193. if (!chart.styledMode && options.colors) {
  33194. this.options.colors = options.colors;
  33195. }
  33196. if (options.plotOptions) {
  33197. merge(true, this.options.plotOptions, options.plotOptions);
  33198. }
  33199. // Maintaining legacy global time. If the chart is instanciated first
  33200. // with global time, then updated with time options, we need to create a
  33201. // new Time instance to avoid mutating the global time (#10536).
  33202. if (options.time && this.time === H.time) {
  33203. this.time = new H.Time(options.time);
  33204. }
  33205. // Some option stuctures correspond one-to-one to chart objects that
  33206. // have update methods, for example
  33207. // options.credits => chart.credits
  33208. // options.legend => chart.legend
  33209. // options.title => chart.title
  33210. // options.tooltip => chart.tooltip
  33211. // options.subtitle => chart.subtitle
  33212. // options.mapNavigation => chart.mapNavigation
  33213. // options.navigator => chart.navigator
  33214. // options.scrollbar => chart.scrollbar
  33215. objectEach(options, function (val, key) {
  33216. if (chart[key] &&
  33217. typeof chart[key].update === 'function') {
  33218. chart[key].update(val, false);
  33219. // If a one-to-one object does not exist, look for an adder function
  33220. }
  33221. else if (typeof chart[adders[key]] === 'function') {
  33222. chart[adders[key]](val);
  33223. }
  33224. if (key !== 'chart' &&
  33225. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  33226. updateAllSeries = true;
  33227. }
  33228. });
  33229. // Setters for collections. For axes and series, each item is referred
  33230. // by an id. If the id is not found, it defaults to the corresponding
  33231. // item in the collection, so setting one series without an id, will
  33232. // update the first series in the chart. Setting two series without
  33233. // an id will update the first and the second respectively (#6019)
  33234. // chart.update and responsive.
  33235. this.collectionsWithUpdate.forEach(function (coll) {
  33236. var indexMap;
  33237. if (options[coll]) {
  33238. // In stock charts, the navigator series are also part of the
  33239. // chart.series array, but those series should not be handled
  33240. // here (#8196).
  33241. if (coll === 'series') {
  33242. indexMap = [];
  33243. chart[coll].forEach(function (s, i) {
  33244. if (!s.options.isInternal) {
  33245. indexMap.push(pick(s.options.index, i));
  33246. }
  33247. });
  33248. }
  33249. splat(options[coll]).forEach(function (newOptions, i) {
  33250. var item = (defined(newOptions.id) &&
  33251. chart.get(newOptions.id)) || chart[coll][indexMap ? indexMap[i] : i];
  33252. if (item && item.coll === coll) {
  33253. item.update(newOptions, false);
  33254. if (oneToOne) {
  33255. item.touched = true;
  33256. }
  33257. }
  33258. // If oneToOne and no matching item is found, add one
  33259. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  33260. chart.collectionsWithInit[coll][0].apply(chart,
  33261. // [newOptions, ...extraArguments, redraw=false]
  33262. [
  33263. newOptions
  33264. ].concat(
  33265. // Not all initializers require extra args
  33266. chart.collectionsWithInit[coll][1] || []).concat([
  33267. false
  33268. ])).touched = true;
  33269. }
  33270. });
  33271. // Add items for removal
  33272. if (oneToOne) {
  33273. chart[coll].forEach(function (item) {
  33274. if (!item.touched && !item.options.isInternal) {
  33275. itemsForRemoval.push(item);
  33276. }
  33277. else {
  33278. delete item.touched;
  33279. }
  33280. });
  33281. }
  33282. }
  33283. });
  33284. itemsForRemoval.forEach(function (item) {
  33285. if (item.remove) {
  33286. item.remove(false);
  33287. }
  33288. });
  33289. if (updateAllAxes) {
  33290. chart.axes.forEach(function (axis) {
  33291. axis.update({}, false);
  33292. });
  33293. }
  33294. // Certain options require the whole series structure to be thrown away
  33295. // and rebuilt
  33296. if (updateAllSeries) {
  33297. chart.getSeriesOrderByLinks().forEach(function (series) {
  33298. // Avoid removed navigator series
  33299. if (series.chart) {
  33300. series.update({}, false);
  33301. }
  33302. }, this);
  33303. }
  33304. // For loading, just update the options, do not redraw
  33305. if (options.loading) {
  33306. merge(true, chart.options.loading, options.loading);
  33307. }
  33308. // Update size. Redraw is forced.
  33309. newWidth = optionsChart && optionsChart.width;
  33310. newHeight = optionsChart && optionsChart.height;
  33311. if (isString(newHeight)) {
  33312. newHeight = relativeLength(newHeight, newWidth || chart.chartWidth);
  33313. }
  33314. if (
  33315. // In this case, run chart.setSize with newWidth and newHeight which
  33316. // are undefined, only for reflowing chart elements because margin
  33317. // or spacing has been set (#8190)
  33318. runSetSize ||
  33319. // In this case, the size is actually set
  33320. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  33321. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  33322. chart.setSize(newWidth, newHeight, animation);
  33323. }
  33324. else if (pick(redraw, true)) {
  33325. chart.redraw(animation);
  33326. }
  33327. fireEvent(chart, 'afterUpdate', {
  33328. options: options,
  33329. redraw: redraw,
  33330. animation: animation
  33331. });
  33332. },
  33333. /**
  33334. * Shortcut to set the subtitle options. This can also be done from {@link
  33335. * Chart#update} or {@link Chart#setTitle}.
  33336. *
  33337. * @function Highcharts.Chart#setSubtitle
  33338. *
  33339. * @param {Highcharts.SubtitleOptions} options
  33340. * New subtitle options. The subtitle text itself is set by the
  33341. * `options.text` property.
  33342. *
  33343. * @return {void}
  33344. */
  33345. setSubtitle: function (options, redraw) {
  33346. this.applyDescription('subtitle', options);
  33347. this.layOutTitles(redraw);
  33348. },
  33349. /**
  33350. * Set the caption options. This can also be done from {@link
  33351. * Chart#update}.
  33352. *
  33353. * @function Highcharts.Chart#setCaption
  33354. *
  33355. * @param {Highcharts.CaptionOptions} options
  33356. * New caption options. The caption text itself is set by the
  33357. * `options.text` property.
  33358. *
  33359. * @return {void}
  33360. */
  33361. setCaption: function (options, redraw) {
  33362. this.applyDescription('caption', options);
  33363. this.layOutTitles(redraw);
  33364. }
  33365. });
  33366. /**
  33367. * These collections (arrays) implement `Chart.addSomethig` method used in
  33368. * chart.update() to create new object in the collection. Equivalent for
  33369. * deleting is resolved by simple `Somethig.remove()`.
  33370. *
  33371. * Note: We need to define these references after initializers are bound to
  33372. * chart's prototype.
  33373. */
  33374. Chart.prototype.collectionsWithInit = {
  33375. // collectionName: [ initializingMethod, [extraArguments] ]
  33376. xAxis: [Chart.prototype.addAxis, [true]],
  33377. yAxis: [Chart.prototype.addAxis, [false]],
  33378. series: [Chart.prototype.addSeries]
  33379. };
  33380. // extend the Point prototype for dynamic methods
  33381. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  33382. /**
  33383. * Update point with new options (typically x/y data) and optionally redraw
  33384. * the series.
  33385. *
  33386. * @sample highcharts/members/point-update-column/
  33387. * Update column value
  33388. * @sample highcharts/members/point-update-pie/
  33389. * Update pie slice
  33390. * @sample maps/members/point-update/
  33391. * Update map area value in Highmaps
  33392. *
  33393. * @function Highcharts.Point#update
  33394. *
  33395. * @param {Highcharts.PointOptionsType} options
  33396. * The point options. Point options are handled as described under
  33397. * the `series.type.data` item for each series type. For example
  33398. * for a line series, if options is a single number, the point will
  33399. * be given that number as the marin y value. If it is an array, it
  33400. * will be interpreted as x and y values respectively. If it is an
  33401. * object, advanced options are applied.
  33402. *
  33403. * @param {boolean} [redraw=true]
  33404. * Whether to redraw the chart after the point is updated. If doing
  33405. * more operations on the chart, it is best practice to set
  33406. * `redraw` to false and call `chart.redraw()` after.
  33407. *
  33408. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  33409. * Whether to apply animation, and optionally animation
  33410. * configuration.
  33411. *
  33412. * @return {void}
  33413. *
  33414. * @fires Highcharts.Point#event:update
  33415. */
  33416. update: function (options, redraw, animation, runEvent) {
  33417. var point = this, series = point.series, graphic = point.graphic, i, chart = series.chart, seriesOptions = series.options;
  33418. redraw = pick(redraw, true);
  33419. /**
  33420. * @private
  33421. */
  33422. function update() {
  33423. point.applyOptions(options);
  33424. // Update visuals
  33425. if (point.y === null && graphic) { // #4146
  33426. point.graphic = graphic.destroy();
  33427. }
  33428. if (isObject(options, true)) {
  33429. // Destroy so we can get new elements
  33430. if (graphic && graphic.element) {
  33431. // "null" is also a valid symbol
  33432. if (options &&
  33433. options.marker &&
  33434. typeof options.marker.symbol !== 'undefined') {
  33435. point.graphic = graphic.destroy();
  33436. }
  33437. }
  33438. if (options && options.dataLabels && point.dataLabel) {
  33439. point.dataLabel = point.dataLabel.destroy(); // #2468
  33440. }
  33441. if (point.connector) {
  33442. point.connector = point.connector.destroy(); // #7243
  33443. }
  33444. }
  33445. // record changes in the parallel arrays
  33446. i = point.index;
  33447. series.updateParallelArrays(point, i);
  33448. // Record the options to options.data. If the old or the new config
  33449. // is an object, use point options, otherwise use raw options
  33450. // (#4701, #4916).
  33451. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  33452. isObject(options, true)) ?
  33453. point.options :
  33454. pick(options, seriesOptions.data[i]);
  33455. // redraw
  33456. series.isDirty = series.isDirtyData = true;
  33457. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  33458. chart.isDirtyBox = true;
  33459. }
  33460. if (seriesOptions.legendType === 'point') { // #1831, #1885
  33461. chart.isDirtyLegend = true;
  33462. }
  33463. if (redraw) {
  33464. chart.redraw(animation);
  33465. }
  33466. }
  33467. // Fire the event with a default handler of doing the update
  33468. if (runEvent === false) { // When called from setData
  33469. update();
  33470. }
  33471. else {
  33472. point.firePointEvent('update', { options: options }, update);
  33473. }
  33474. },
  33475. /**
  33476. * Remove a point and optionally redraw the series and if necessary the axes
  33477. *
  33478. * @sample highcharts/plotoptions/series-point-events-remove/
  33479. * Remove point and confirm
  33480. * @sample highcharts/members/point-remove/
  33481. * Remove pie slice
  33482. * @sample maps/members/point-remove/
  33483. * Remove selected points in Highmaps
  33484. *
  33485. * @function Highcharts.Point#remove
  33486. *
  33487. * @param {boolean} [redraw=true]
  33488. * Whether to redraw the chart or wait for an explicit call. When
  33489. * doing more operations on the chart, for example running
  33490. * `point.remove()` in a loop, it is best practice to set `redraw`
  33491. * to false and call `chart.redraw()` after.
  33492. *
  33493. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
  33494. * Whether to apply animation, and optionally animation
  33495. * configuration.
  33496. *
  33497. * @return {void}
  33498. */
  33499. remove: function (redraw, animation) {
  33500. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  33501. }
  33502. });
  33503. // Extend the series prototype for dynamic methods
  33504. extend(Series.prototype, /** @lends Series.prototype */ {
  33505. /**
  33506. * Add a point to the series after render time. The point can be added at
  33507. * the end, or by giving it an X value, to the start or in the middle of the
  33508. * series.
  33509. *
  33510. * @sample highcharts/members/series-addpoint-append/
  33511. * Append point
  33512. * @sample highcharts/members/series-addpoint-append-and-shift/
  33513. * Append and shift
  33514. * @sample highcharts/members/series-addpoint-x-and-y/
  33515. * Both X and Y values given
  33516. * @sample highcharts/members/series-addpoint-pie/
  33517. * Append pie slice
  33518. * @sample stock/members/series-addpoint/
  33519. * Append 100 points in Highstock
  33520. * @sample stock/members/series-addpoint-shift/
  33521. * Append and shift in Highstock
  33522. * @sample maps/members/series-addpoint/
  33523. * Add a point in Highmaps
  33524. *
  33525. * @function Highcharts.Series#addPoint
  33526. *
  33527. * @param {Highcharts.PointOptionsType} options
  33528. * The point options. If options is a single number, a point with
  33529. * that y value is appended to the series. If it is an array, it will
  33530. * be interpreted as x and y values respectively. If it is an
  33531. * object, advanced options as outlined under `series.data` are
  33532. * applied.
  33533. *
  33534. * @param {boolean} [redraw=true]
  33535. * Whether to redraw the chart after the point is added. When adding
  33536. * more than one point, it is highly recommended that the redraw
  33537. * option be set to false, and instead {@link Chart#redraw} is
  33538. * explicitly called after the adding of points is finished.
  33539. * Otherwise, the chart will redraw after adding each point.
  33540. *
  33541. * @param {boolean} [shift=false]
  33542. * If true, a point is shifted off the start of the series as one is
  33543. * appended to the end.
  33544. *
  33545. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33546. * Whether to apply animation, and optionally animation
  33547. * configuration.
  33548. *
  33549. * @param {boolean} [withEvent=true]
  33550. * Used internally, whether to fire the series `addPoint` event.
  33551. *
  33552. * @return {void}
  33553. *
  33554. * @fires Highcharts.Series#event:addPoint
  33555. */
  33556. addPoint: function (options, redraw, shift, animation, withEvent) {
  33557. 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;
  33558. // Optional redraw, defaults to true
  33559. redraw = pick(redraw, true);
  33560. // Get options and push the point to xData, yData and series.options. In
  33561. // series.generatePoints the Point instance will be created on demand
  33562. // and pushed to the series.data array.
  33563. point = { series: series };
  33564. series.pointClass.prototype.applyOptions.apply(point, [options]);
  33565. x = point.x;
  33566. // Get the insertion point
  33567. i = xData.length;
  33568. if (series.requireSorting && x < xData[i - 1]) {
  33569. isInTheMiddle = true;
  33570. while (i && xData[i - 1] > x) {
  33571. i--;
  33572. }
  33573. }
  33574. // Insert undefined item
  33575. series.updateParallelArrays(point, 'splice', i, 0, 0);
  33576. // Update it
  33577. series.updateParallelArrays(point, i);
  33578. if (names && point.name) {
  33579. names[x] = point.name;
  33580. }
  33581. dataOptions.splice(i, 0, options);
  33582. if (isInTheMiddle) {
  33583. series.data.splice(i, 0, null);
  33584. series.processData();
  33585. }
  33586. // Generate points to be added to the legend (#1329)
  33587. if (seriesOptions.legendType === 'point') {
  33588. series.generatePoints();
  33589. }
  33590. // Shift the first point off the parallel arrays
  33591. if (shift) {
  33592. if (data[0] && data[0].remove) {
  33593. data[0].remove(false);
  33594. }
  33595. else {
  33596. data.shift();
  33597. series.updateParallelArrays(point, 'shift');
  33598. dataOptions.shift();
  33599. }
  33600. }
  33601. // Fire event
  33602. if (withEvent !== false) {
  33603. fireEvent(series, 'addPoint', { point: point });
  33604. }
  33605. // redraw
  33606. series.isDirty = true;
  33607. series.isDirtyData = true;
  33608. if (redraw) {
  33609. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  33610. }
  33611. },
  33612. /**
  33613. * Remove a point from the series. Unlike the
  33614. * {@link Highcharts.Point#remove} method, this can also be done on a point
  33615. * that is not instanciated because it is outside the view or subject to
  33616. * Highstock data grouping.
  33617. *
  33618. * @sample highcharts/members/series-removepoint/
  33619. * Remove cropped point
  33620. *
  33621. * @function Highcharts.Series#removePoint
  33622. *
  33623. * @param {number} i
  33624. * The index of the point in the {@link Highcharts.Series.data|data}
  33625. * array.
  33626. *
  33627. * @param {boolean} [redraw=true]
  33628. * Whether to redraw the chart after the point is added. When
  33629. * removing more than one point, it is highly recommended that the
  33630. * `redraw` option be set to `false`, and instead {@link
  33631. * Highcharts.Chart#redraw} is explicitly called after the adding of
  33632. * points is finished.
  33633. *
  33634. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33635. * Whether and optionally how the series should be animated.
  33636. *
  33637. * @return {void}
  33638. *
  33639. * @fires Highcharts.Point#event:remove
  33640. */
  33641. removePoint: function (i, redraw, animation) {
  33642. var series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  33643. if (points && points.length === data.length) { // #4935
  33644. points.splice(i, 1);
  33645. }
  33646. data.splice(i, 1);
  33647. series.options.data.splice(i, 1);
  33648. series.updateParallelArrays(point || { series: series }, 'splice', i, 1);
  33649. if (point) {
  33650. point.destroy();
  33651. }
  33652. // redraw
  33653. series.isDirty = true;
  33654. series.isDirtyData = true;
  33655. if (redraw) {
  33656. chart.redraw();
  33657. }
  33658. };
  33659. setAnimation(animation, chart);
  33660. redraw = pick(redraw, true);
  33661. // Fire the event with a default handler of removing the point
  33662. if (point) {
  33663. point.firePointEvent('remove', null, remove);
  33664. }
  33665. else {
  33666. remove();
  33667. }
  33668. },
  33669. /**
  33670. * Remove a series and optionally redraw the chart.
  33671. *
  33672. * @sample highcharts/members/series-remove/
  33673. * Remove first series from a button
  33674. *
  33675. * @function Highcharts.Series#remove
  33676. *
  33677. * @param {boolean} [redraw=true]
  33678. * Whether to redraw the chart or wait for an explicit call to
  33679. * {@link Highcharts.Chart#redraw}.
  33680. *
  33681. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33682. * Whether to apply animation, and optionally animation
  33683. * configuration.
  33684. *
  33685. * @param {boolean} [withEvent=true]
  33686. * Used internally, whether to fire the series `remove` event.
  33687. *
  33688. * @return {void}
  33689. *
  33690. * @fires Highcharts.Series#event:remove
  33691. */
  33692. remove: function (redraw, animation, withEvent, keepEvents) {
  33693. var series = this, chart = series.chart;
  33694. /**
  33695. * @private
  33696. */
  33697. function remove() {
  33698. // Destroy elements
  33699. series.destroy(keepEvents);
  33700. series.remove = null; // Prevent from doing again (#9097)
  33701. // Redraw
  33702. chart.isDirtyLegend = chart.isDirtyBox = true;
  33703. chart.linkSeries();
  33704. if (pick(redraw, true)) {
  33705. chart.redraw(animation);
  33706. }
  33707. }
  33708. // Fire the event with a default handler of removing the point
  33709. if (withEvent !== false) {
  33710. fireEvent(series, 'remove', null, remove);
  33711. }
  33712. else {
  33713. remove();
  33714. }
  33715. },
  33716. /**
  33717. * Update the series with a new set of options. For a clean and precise
  33718. * handling of new options, all methods and elements from the series are
  33719. * removed, and it is initialized from scratch. Therefore, this method is
  33720. * more performance expensive than some other utility methods like {@link
  33721. * Series#setData} or {@link Series#setVisible}.
  33722. *
  33723. * Note that `Series.update` may mutate the passed `data` options.
  33724. *
  33725. * @sample highcharts/members/series-update/
  33726. * Updating series options
  33727. * @sample maps/members/series-update/
  33728. * Update series options in Highmaps
  33729. *
  33730. * @function Highcharts.Series#update
  33731. *
  33732. * @param {Highcharts.SeriesOptionsType} options
  33733. * New options that will be merged with the series' existing options.
  33734. *
  33735. * @param {boolean} [redraw=true]
  33736. * Whether to redraw the chart after the series is altered. If doing
  33737. * more operations on the chart, it is a good idea to set redraw to
  33738. * false and call {@link Chart#redraw} after.
  33739. *
  33740. * @return {void}
  33741. *
  33742. * @fires Highcharts.Series#event:update
  33743. * @fires Highcharts.Series#event:afterUpdate
  33744. */
  33745. update: function (options, redraw) {
  33746. options = H.cleanRecursively(options, this.userOptions);
  33747. fireEvent(this, 'update', { options: options });
  33748. var series = this, chart = series.chart,
  33749. // must use user options when changing type because series.options
  33750. // is merged in with type specific plotOptions
  33751. oldOptions = series.userOptions, seriesOptions, initialType = series.initialType || series.type, newType = (options.type ||
  33752. oldOptions.type ||
  33753. chart.options.chart.type), keepPoints = !(
  33754. // Indicators, histograms etc recalculate the data. It should be
  33755. // possible to omit this.
  33756. this.hasDerivedData ||
  33757. // Changes to data grouping requires new points in new groups
  33758. options.dataGrouping ||
  33759. // New type requires new point classes
  33760. (newType && newType !== this.type) ||
  33761. // New options affecting how the data points are built
  33762. typeof options.pointStart !== 'undefined' ||
  33763. options.pointInterval ||
  33764. options.pointIntervalUnit ||
  33765. options.keys), initialSeriesProto = seriesTypes[initialType].prototype, n, groups = [
  33766. 'group',
  33767. 'markerGroup',
  33768. 'dataLabelsGroup',
  33769. 'transformGroup'
  33770. ], preserve = [
  33771. 'eventOptions',
  33772. 'navigatorSeries',
  33773. 'baseSeries'
  33774. ],
  33775. // Animation must be enabled when calling update before the initial
  33776. // animation has first run. This happens when calling update
  33777. // directly after chart initialization, or when applying responsive
  33778. // rules (#6912).
  33779. animation = series.finishedAnimating && { animation: false }, kinds = {};
  33780. if (keepPoints) {
  33781. preserve.push('data', 'isDirtyData', 'points', 'processedXData', 'processedYData', 'xIncrement', '_hasPointMarkers', '_hasPointLabels',
  33782. // Map specific, consider moving it to series-specific preserve-
  33783. // properties (#10617)
  33784. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  33785. if (options.visible !== false) {
  33786. preserve.push('area', 'graph');
  33787. }
  33788. series.parallelArrays.forEach(function (key) {
  33789. preserve.push(key + 'Data');
  33790. });
  33791. if (options.data) {
  33792. // setData uses dataSorting options so we need to update them
  33793. // earlier
  33794. if (options.dataSorting) {
  33795. extend(series.options.dataSorting, options.dataSorting);
  33796. }
  33797. this.setData(options.data, false);
  33798. }
  33799. }
  33800. // Do the merge, with some forced options
  33801. options = merge(oldOptions, animation, {
  33802. // When oldOptions.index is null it should't be cleared.
  33803. // Otherwise navigator series will have wrong indexes (#10193).
  33804. index: typeof oldOptions.index === 'undefined' ?
  33805. series.index : oldOptions.index,
  33806. pointStart: pick(
  33807. // when updating from blank (#7933)
  33808. oldOptions.pointStart,
  33809. // when updating after addPoint
  33810. series.xData[0])
  33811. }, (!keepPoints && { data: series.options.data }), options);
  33812. // Merge does not merge arrays, but replaces them. Since points were
  33813. // updated, `series.options.data` has correct merged options, use it:
  33814. if (keepPoints && options.data) {
  33815. options.data = series.options.data;
  33816. }
  33817. // Make sure preserved properties are not destroyed (#3094)
  33818. preserve = groups.concat(preserve);
  33819. preserve.forEach(function (prop) {
  33820. preserve[prop] = series[prop];
  33821. delete series[prop];
  33822. });
  33823. // Destroy the series and delete all properties. Reinsert all
  33824. // methods and properties from the new type prototype (#2270,
  33825. // #3719).
  33826. series.remove(false, null, false, true);
  33827. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  33828. series[n] = void 0;
  33829. }
  33830. if (seriesTypes[newType || initialType]) {
  33831. extend(series, seriesTypes[newType || initialType].prototype);
  33832. }
  33833. else {
  33834. H.error(17, true, chart, { missingModuleFor: (newType || initialType) });
  33835. }
  33836. // Re-register groups (#3094) and other preserved properties
  33837. preserve.forEach(function (prop) {
  33838. series[prop] = preserve[prop];
  33839. });
  33840. series.init(chart, options);
  33841. // Remove particular elements of the points. Check `series.options`
  33842. // because we need to consider the options being set on plotOptions as
  33843. // well.
  33844. if (keepPoints && this.points) {
  33845. seriesOptions = series.options;
  33846. // What kind of elements to destroy
  33847. if (seriesOptions.visible === false) {
  33848. kinds.graphic = 1;
  33849. kinds.dataLabel = 1;
  33850. }
  33851. else if (!series._hasPointLabels) {
  33852. var marker = seriesOptions.marker, dataLabels = seriesOptions.dataLabels;
  33853. if (marker && (marker.enabled === false ||
  33854. 'symbol' in marker // #10870
  33855. )) {
  33856. kinds.graphic = 1;
  33857. }
  33858. if (dataLabels &&
  33859. dataLabels.enabled === false) {
  33860. kinds.dataLabel = 1;
  33861. }
  33862. }
  33863. this.points.forEach(function (point) {
  33864. if (point && point.series) {
  33865. point.resolveColor();
  33866. // Destroy elements in order to recreate based on updated
  33867. // series options.
  33868. if (Object.keys(kinds).length) {
  33869. point.destroyElements(kinds);
  33870. }
  33871. if (seriesOptions.showInLegend === false &&
  33872. point.legendItem) {
  33873. chart.legend.destroyItem(point);
  33874. }
  33875. }
  33876. }, this);
  33877. }
  33878. // Update the Z index of groups (#3380, #7397)
  33879. if (options.zIndex !== oldOptions.zIndex) {
  33880. groups.forEach(function (groupName) {
  33881. if (series[groupName]) {
  33882. series[groupName].attr({
  33883. zIndex: options.zIndex
  33884. });
  33885. }
  33886. });
  33887. }
  33888. series.initialType = initialType;
  33889. chart.linkSeries(); // Links are lost in series.remove (#3028)
  33890. fireEvent(this, 'afterUpdate');
  33891. if (pick(redraw, true)) {
  33892. chart.redraw(keepPoints ? void 0 : false);
  33893. }
  33894. },
  33895. /**
  33896. * Used from within series.update
  33897. *
  33898. * @private
  33899. * @function Highcharts.Series#setName
  33900. *
  33901. * @param {string} name
  33902. *
  33903. * @return {void}
  33904. */
  33905. setName: function (name) {
  33906. this.name = this.options.name = this.userOptions.name = name;
  33907. this.chart.isDirtyLegend = true;
  33908. }
  33909. });
  33910. // Extend the Axis.prototype for dynamic methods
  33911. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  33912. /**
  33913. * Update an axis object with a new set of options. The options are merged
  33914. * with the existing options, so only new or altered options need to be
  33915. * specified.
  33916. *
  33917. * @sample highcharts/members/axis-update/
  33918. * Axis update demo
  33919. *
  33920. * @function Highcharts.Axis#update
  33921. *
  33922. * @param {Highcharts.AxisOptions} options
  33923. * The new options that will be merged in with existing options on
  33924. * the axis.
  33925. *
  33926. * @param {boolean} [redraw=true]
  33927. * Whether to redraw the chart after the axis is altered. If doing
  33928. * more operations on the chart, it is a good idea to set redraw to
  33929. * false and call {@link Chart#redraw} after.
  33930. *
  33931. * @return {void}
  33932. */
  33933. update: function (options, redraw) {
  33934. var chart = this.chart, newEvents = ((options && options.events) || {});
  33935. options = merge(this.userOptions, options);
  33936. // Color Axis is not an array,
  33937. // This change is applied in the ColorAxis wrapper
  33938. if (chart.options[this.coll].indexOf) {
  33939. // Don't use this.options.index,
  33940. // StockChart has Axes in navigator too
  33941. chart.options[this.coll][chart.options[this.coll].indexOf(this.userOptions)] = options;
  33942. }
  33943. // Remove old events, if no new exist (#8161)
  33944. objectEach(chart.options[this.coll].events, function (fn, ev) {
  33945. if (typeof newEvents[ev] === 'undefined') {
  33946. newEvents[ev] = void 0;
  33947. }
  33948. });
  33949. this.destroy(true);
  33950. this.init(chart, extend(options, { events: newEvents }));
  33951. chart.isDirtyBox = true;
  33952. if (pick(redraw, true)) {
  33953. chart.redraw();
  33954. }
  33955. },
  33956. /**
  33957. * Remove the axis from the chart.
  33958. *
  33959. * @sample highcharts/members/chart-addaxis/
  33960. * Add and remove axes
  33961. *
  33962. * @function Highcharts.Axis#remove
  33963. *
  33964. * @param {boolean} [redraw=true]
  33965. * Whether to redraw the chart following the remove.
  33966. *
  33967. * @return {void}
  33968. */
  33969. remove: function (redraw) {
  33970. var chart = this.chart, key = this.coll, // xAxis or yAxis
  33971. axisSeries = this.series, i = axisSeries.length;
  33972. // Remove associated series (#2687)
  33973. while (i--) {
  33974. if (axisSeries[i]) {
  33975. axisSeries[i].remove(false);
  33976. }
  33977. }
  33978. // Remove the axis
  33979. erase(chart.axes, this);
  33980. erase(chart[key], this);
  33981. if (isArray(chart.options[key])) {
  33982. chart.options[key].splice(this.options.index, 1);
  33983. }
  33984. else { // color axis, #6488
  33985. delete chart.options[key];
  33986. }
  33987. chart[key].forEach(function (axis, i) {
  33988. // Re-index, #1706, #8075
  33989. axis.options.index = axis.userOptions.index = i;
  33990. });
  33991. this.destroy();
  33992. chart.isDirtyBox = true;
  33993. if (pick(redraw, true)) {
  33994. chart.redraw();
  33995. }
  33996. },
  33997. /**
  33998. * Update the axis title by options after render time.
  33999. *
  34000. * @sample highcharts/members/axis-settitle/
  34001. * Set a new Y axis title
  34002. *
  34003. * @function Highcharts.Axis#setTitle
  34004. *
  34005. * @param {Highcharts.AxisTitleOptions} titleOptions
  34006. * The additional title options.
  34007. *
  34008. * @param {boolean} [redraw=true]
  34009. * Whether to redraw the chart after setting the title.
  34010. *
  34011. * @return {void}
  34012. */
  34013. setTitle: function (titleOptions, redraw) {
  34014. this.update({ title: titleOptions }, redraw);
  34015. },
  34016. /**
  34017. * Set new axis categories and optionally redraw.
  34018. *
  34019. * @sample highcharts/members/axis-setcategories/
  34020. * Set categories by click on a button
  34021. *
  34022. * @function Highcharts.Axis#setCategories
  34023. *
  34024. * @param {Array<string>} categories
  34025. * The new categories.
  34026. *
  34027. * @param {boolean} [redraw=true]
  34028. * Whether to redraw the chart.
  34029. *
  34030. * @return {void}
  34031. */
  34032. setCategories: function (categories, redraw) {
  34033. this.update({ categories: categories }, redraw);
  34034. }
  34035. });
  34036. });
  34037. _registerModule(_modules, 'parts/AreaSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  34038. /* *
  34039. *
  34040. * (c) 2010-2019 Torstein Honsi
  34041. *
  34042. * License: www.highcharts.com/license
  34043. *
  34044. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34045. *
  34046. * */
  34047. var objectEach = U.objectEach, pick = U.pick;
  34048. var color = H.color, LegendSymbolMixin = H.LegendSymbolMixin, Series = H.Series, seriesType = H.seriesType;
  34049. /**
  34050. * Area series type.
  34051. *
  34052. * @private
  34053. * @class
  34054. * @name Highcharts.seriesTypes.area
  34055. *
  34056. * @augments Highcharts.Series
  34057. */
  34058. seriesType('area', 'line',
  34059. /**
  34060. * The area series type.
  34061. *
  34062. * @sample {highcharts} highcharts/demo/area-basic/
  34063. * Area chart
  34064. * @sample {highstock} stock/demo/area/
  34065. * Area chart
  34066. *
  34067. * @extends plotOptions.line
  34068. * @excluding useOhlcData
  34069. * @product highcharts highstock
  34070. * @optionparent plotOptions.area
  34071. */
  34072. {
  34073. /**
  34074. * Fill color or gradient for the area. When `null`, the series' `color`
  34075. * is used with the series' `fillOpacity`.
  34076. *
  34077. * In styled mode, the fill color can be set with the `.highcharts-area`
  34078. * class name.
  34079. *
  34080. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  34081. * Null by default
  34082. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  34083. * Gradient
  34084. *
  34085. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  34086. * @product highcharts highstock
  34087. * @apioption plotOptions.area.fillColor
  34088. */
  34089. /**
  34090. * Fill opacity for the area. When you set an explicit `fillColor`,
  34091. * the `fillOpacity` is not applied. Instead, you should define the
  34092. * opacity in the `fillColor` with an rgba color definition. The
  34093. * `fillOpacity` setting, also the default setting, overrides the alpha
  34094. * component of the `color` setting.
  34095. *
  34096. * In styled mode, the fill opacity can be set with the
  34097. * `.highcharts-area` class name.
  34098. *
  34099. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  34100. * Automatic fill color and fill opacity of 0.1
  34101. *
  34102. * @type {number}
  34103. * @default {highcharts} 0.75
  34104. * @default {highstock} 0.75
  34105. * @product highcharts highstock
  34106. * @apioption plotOptions.area.fillOpacity
  34107. */
  34108. /**
  34109. * A separate color for the graph line. By default the line takes the
  34110. * `color` of the series, but the lineColor setting allows setting a
  34111. * separate color for the line without altering the `fillColor`.
  34112. *
  34113. * In styled mode, the line stroke can be set with the
  34114. * `.highcharts-graph` class name.
  34115. *
  34116. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  34117. * Dark gray line
  34118. *
  34119. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  34120. * @product highcharts highstock
  34121. * @apioption plotOptions.area.lineColor
  34122. */
  34123. /**
  34124. * A separate color for the negative part of the area.
  34125. *
  34126. * In styled mode, a negative color is set with the
  34127. * `.highcharts-negative` class name.
  34128. *
  34129. * @see [negativeColor](#plotOptions.area.negativeColor)
  34130. *
  34131. * @sample {highcharts} highcharts/css/series-negative-color/
  34132. * Negative color in styled mode
  34133. *
  34134. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  34135. * @since 3.0
  34136. * @product highcharts
  34137. * @apioption plotOptions.area.negativeFillColor
  34138. */
  34139. /**
  34140. * Whether the whole area or just the line should respond to mouseover
  34141. * tooltips and other mouse or touch events.
  34142. *
  34143. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  34144. * Display the tooltip when the area is hovered
  34145. *
  34146. * @type {boolean}
  34147. * @default false
  34148. * @since 1.1.6
  34149. * @product highcharts highstock
  34150. * @apioption plotOptions.area.trackByArea
  34151. */
  34152. /**
  34153. * When this is true, the series will not cause the Y axis to cross
  34154. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  34155. * unless the data actually crosses the plane.
  34156. *
  34157. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  34158. * 3 will make the Y axis show negative values according to the
  34159. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  34160. * at 0.
  34161. *
  34162. * @since 4.1.9
  34163. * @product highcharts highstock
  34164. */
  34165. softThreshold: false,
  34166. /**
  34167. * The Y axis value to serve as the base for the area, for
  34168. * distinguishing between values above and below a threshold. The area
  34169. * between the graph and the threshold is filled.
  34170. *
  34171. * * If a number is given, the Y axis will scale to the threshold.
  34172. * * If `null`, the scaling behaves like a line series with fill between
  34173. * the graph and the Y axis minimum.
  34174. * * If `Infinity` or `-Infinity`, the area between the graph and the
  34175. * corresponing Y axis extreme is filled (since v6.1.0).
  34176. *
  34177. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  34178. * A threshold of 100
  34179. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  34180. * A threshold of Infinity
  34181. *
  34182. * @since 2.0
  34183. * @product highcharts highstock
  34184. */
  34185. threshold: 0
  34186. },
  34187. /* eslint-disable valid-jsdoc */
  34188. /**
  34189. * @lends seriesTypes.area.prototype
  34190. */
  34191. {
  34192. singleStacks: false,
  34193. /**
  34194. * Return an array of stacked points, where null and missing points are
  34195. * replaced by dummy points in order for gaps to be drawn correctly in
  34196. * stacks.
  34197. * @private
  34198. */
  34199. getStackPoints: function (points) {
  34200. var series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacks[this.stackKey], pointMap = {}, seriesIndex = series.index, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, visibleSeries, upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1, i;
  34201. points = points || this.points;
  34202. if (this.options.stacking) {
  34203. for (i = 0; i < points.length; i++) {
  34204. // Reset after point update (#7326)
  34205. points[i].leftNull = points[i].rightNull = void 0;
  34206. // Create a map where we can quickly look up the points by
  34207. // their X values.
  34208. pointMap[points[i].x] = points[i];
  34209. }
  34210. // Sort the keys (#1651)
  34211. objectEach(stack, function (stackX, x) {
  34212. // nulled after switching between
  34213. // grouping and not (#1651, #2336)
  34214. if (stackX.total !== null) {
  34215. keys.push(x);
  34216. }
  34217. });
  34218. keys.sort(function (a, b) {
  34219. return a - b;
  34220. });
  34221. visibleSeries = yAxisSeries.map(function (s) {
  34222. return s.visible;
  34223. });
  34224. keys.forEach(function (x, idx) {
  34225. var y = 0, stackPoint, stackedValues;
  34226. if (pointMap[x] && !pointMap[x].isNull) {
  34227. segment.push(pointMap[x]);
  34228. // Find left and right cliff. -1 goes left, 1 goes
  34229. // right.
  34230. [-1, 1].forEach(function (direction) {
  34231. var nullName = direction === 1 ?
  34232. 'rightNull' :
  34233. 'leftNull', cliffName = direction === 1 ?
  34234. 'rightCliff' :
  34235. 'leftCliff', cliff = 0, otherStack = stack[keys[idx + direction]];
  34236. // If there is a stack next to this one,
  34237. // to the left or to the right...
  34238. if (otherStack) {
  34239. i = seriesIndex;
  34240. // Can go either up or down,
  34241. // depending on reversedStacks
  34242. while (i >= 0 && i < seriesLength) {
  34243. stackPoint = otherStack.points[i];
  34244. if (!stackPoint) {
  34245. // If the next point in this series
  34246. // is missing, mark the point
  34247. // with point.leftNull or
  34248. // point.rightNull = true.
  34249. if (i === seriesIndex) {
  34250. pointMap[x][nullName] =
  34251. true;
  34252. // If there are missing points in
  34253. // the next stack in any of the
  34254. // series below this one, we need
  34255. // to substract the missing values
  34256. // and add a hiatus to the left or
  34257. // right.
  34258. }
  34259. else if (visibleSeries[i]) {
  34260. stackedValues =
  34261. stack[x].points[i];
  34262. if (stackedValues) {
  34263. cliff -=
  34264. stackedValues[1] -
  34265. stackedValues[0];
  34266. }
  34267. }
  34268. }
  34269. // When reversedStacks is true, loop up,
  34270. // else loop down
  34271. i += upOrDown;
  34272. }
  34273. }
  34274. pointMap[x][cliffName] = cliff;
  34275. });
  34276. // There is no point for this X value in this series, so we
  34277. // insert a dummy point in order for the areas to be drawn
  34278. // correctly.
  34279. }
  34280. else {
  34281. // Loop down the stack to find the series below this
  34282. // one that has a value (#1991)
  34283. i = seriesIndex;
  34284. while (i >= 0 && i < seriesLength) {
  34285. stackPoint = stack[x].points[i];
  34286. if (stackPoint) {
  34287. y = stackPoint[1];
  34288. break;
  34289. }
  34290. // When reversedStacks is true, loop up, else loop
  34291. // down
  34292. i += upOrDown;
  34293. }
  34294. y = yAxis.translate(// #6272
  34295. y, 0, 1, 0, 1);
  34296. segment.push({
  34297. isNull: true,
  34298. plotX: xAxis.translate(// #6272
  34299. x, 0, 0, 0, 1),
  34300. x: x,
  34301. plotY: y,
  34302. yBottom: y
  34303. });
  34304. }
  34305. });
  34306. }
  34307. return segment;
  34308. },
  34309. /**
  34310. * @private
  34311. */
  34312. getGraphPath: function (points) {
  34313. 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.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  34314. yAxis.getThreshold(options.threshold)), isNull, yBottom, connectNulls = pick(// #10574
  34315. options.connectNulls, stacking === 'percent'),
  34316. // To display null points in underlying stacked series, this
  34317. // series graph must be broken, and the area also fall down to
  34318. // fill the gap left by the null point. #2069
  34319. addDummyPoints = function (i, otherI, side) {
  34320. var point = points[i], stackedValues = stacking &&
  34321. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0, top, bottom, isNull = true;
  34322. if (cliffVal || nullVal) {
  34323. top = (nullVal ?
  34324. stackedValues[0] :
  34325. stackedValues[1]) + cliffVal;
  34326. bottom = stackedValues[0] + cliffVal;
  34327. isNull = !!nullVal;
  34328. }
  34329. else if (!stacking &&
  34330. points[otherI] &&
  34331. points[otherI].isNull) {
  34332. top = bottom = threshold;
  34333. }
  34334. // Add to the top and bottom line of the area
  34335. if (typeof top !== 'undefined') {
  34336. graphPoints.push({
  34337. plotX: plotX,
  34338. plotY: top === null ?
  34339. translatedThreshold :
  34340. yAxis.getThreshold(top),
  34341. isNull: isNull,
  34342. isCliff: true
  34343. });
  34344. bottomPoints.push({
  34345. plotX: plotX,
  34346. plotY: bottom === null ?
  34347. translatedThreshold :
  34348. yAxis.getThreshold(bottom),
  34349. doCurve: false // #1041, gaps in areaspline areas
  34350. });
  34351. }
  34352. };
  34353. // Find what points to use
  34354. points = points || this.points;
  34355. // Fill in missing points
  34356. if (stacking) {
  34357. points = this.getStackPoints(points);
  34358. }
  34359. for (i = 0; i < points.length; i++) {
  34360. // Reset after series.update of stacking property (#12033)
  34361. if (!stacking) {
  34362. points[i].leftCliff = points[i].rightCliff =
  34363. points[i].leftNull = points[i].rightNull = void 0;
  34364. }
  34365. isNull = points[i].isNull;
  34366. plotX = pick(points[i].rectPlotX, points[i].plotX);
  34367. yBottom = pick(points[i].yBottom, translatedThreshold);
  34368. if (!isNull || connectNulls) {
  34369. if (!connectNulls) {
  34370. addDummyPoints(i, i - 1, 'left');
  34371. }
  34372. // Skip null point when stacking is false and connectNulls
  34373. // true
  34374. if (!(isNull && !stacking && connectNulls)) {
  34375. graphPoints.push(points[i]);
  34376. bottomPoints.push({
  34377. x: i,
  34378. plotX: plotX,
  34379. plotY: yBottom
  34380. });
  34381. }
  34382. if (!connectNulls) {
  34383. addDummyPoints(i, i + 1, 'right');
  34384. }
  34385. }
  34386. }
  34387. topPath = getGraphPath.call(this, graphPoints, true, true);
  34388. bottomPoints.reversed = true;
  34389. bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  34390. if (bottomPath.length) {
  34391. bottomPath[0] = 'L';
  34392. }
  34393. areaPath = topPath.concat(bottomPath);
  34394. // TODO: don't set leftCliff and rightCliff when connectNulls?
  34395. graphPath = getGraphPath
  34396. .call(this, graphPoints, false, connectNulls);
  34397. areaPath.xMap = topPath.xMap;
  34398. this.areaPath = areaPath;
  34399. return graphPath;
  34400. },
  34401. /**
  34402. * Draw the graph and the underlying area. This method calls the Series
  34403. * base function and adds the area. The areaPath is calculated in the
  34404. * getSegmentPath method called from Series.prototype.drawGraph.
  34405. * @private
  34406. */
  34407. drawGraph: function () {
  34408. // Define or reset areaPath
  34409. this.areaPath = [];
  34410. // Call the base method
  34411. Series.prototype.drawGraph.apply(this);
  34412. // Define local variables
  34413. var series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  34414. 'area',
  34415. 'highcharts-area',
  34416. this.color,
  34417. options.fillColor
  34418. ]]; // area name, main color, fill color
  34419. zones.forEach(function (zone, i) {
  34420. props.push([
  34421. 'zone-area-' + i,
  34422. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  34423. zone.className,
  34424. zone.color || series.color,
  34425. zone.fillColor || options.fillColor
  34426. ]);
  34427. });
  34428. props.forEach(function (prop) {
  34429. var areaKey = prop[0], area = series[areaKey], verb = area ? 'animate' : 'attr', attribs = {};
  34430. // Create or update the area
  34431. if (area) { // update
  34432. area.endX = series.preventGraphAnimation ?
  34433. null :
  34434. areaPath.xMap;
  34435. area.animate({ d: areaPath });
  34436. }
  34437. else { // create
  34438. attribs.zIndex = 0; // #1069
  34439. area = series[areaKey] = series.chart.renderer
  34440. .path(areaPath)
  34441. .addClass(prop[1])
  34442. .add(series.group);
  34443. area.isArea = true;
  34444. }
  34445. if (!series.chart.styledMode) {
  34446. attribs.fill = pick(prop[3], color(prop[2])
  34447. .setOpacity(pick(options.fillOpacity, 0.75))
  34448. .get());
  34449. }
  34450. area[verb](attribs);
  34451. area.startX = areaPath.xMap;
  34452. area.shiftUnit = options.step ? 2 : 1;
  34453. });
  34454. },
  34455. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  34456. });
  34457. /* eslint-enable valid-jsdoc */
  34458. /**
  34459. * A `area` series. If the [type](#series.area.type) option is not
  34460. * specified, it is inherited from [chart.type](#chart.type).
  34461. *
  34462. * @extends series,plotOptions.area
  34463. * @excluding dataParser, dataURL, useOhlcData
  34464. * @product highcharts highstock
  34465. * @apioption series.area
  34466. */
  34467. /**
  34468. * An array of data points for the series. For the `area` series type,
  34469. * points can be given in the following ways:
  34470. *
  34471. * 1. An array of numerical values. In this case, the numerical values will be
  34472. * interpreted as `y` options. The `x` values will be automatically
  34473. * calculated, either starting at 0 and incremented by 1, or from
  34474. * `pointStart` * and `pointInterval` given in the series options. If the
  34475. * axis has categories, these will be used. Example:
  34476. * ```js
  34477. * data: [0, 5, 3, 5]
  34478. * ```
  34479. *
  34480. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34481. * `x,y`. If the first value is a string, it is applied as the name of the
  34482. * point, and the `x` value is inferred.
  34483. * ```js
  34484. * data: [
  34485. * [0, 9],
  34486. * [1, 7],
  34487. * [2, 6]
  34488. * ]
  34489. * ```
  34490. *
  34491. * 3. An array of objects with named values. The following snippet shows only a
  34492. * few settings, see the complete options set below. If the total number of
  34493. * data points exceeds the series'
  34494. * [turboThreshold](#series.area.turboThreshold), this option is not
  34495. * available.
  34496. * ```js
  34497. * data: [{
  34498. * x: 1,
  34499. * y: 9,
  34500. * name: "Point2",
  34501. * color: "#00FF00"
  34502. * }, {
  34503. * x: 1,
  34504. * y: 6,
  34505. * name: "Point1",
  34506. * color: "#FF00FF"
  34507. * }]
  34508. * ```
  34509. *
  34510. * @sample {highcharts} highcharts/chart/reflow-true/
  34511. * Numerical values
  34512. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34513. * Arrays of numeric x and y
  34514. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34515. * Arrays of datetime x and y
  34516. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34517. * Arrays of point.name and y
  34518. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34519. * Config objects
  34520. *
  34521. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  34522. * @extends series.line.data
  34523. * @product highcharts highstock
  34524. * @apioption series.area.data
  34525. */
  34526. ''; // adds doclets above to transpilat
  34527. });
  34528. _registerModule(_modules, 'parts/SplineSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  34529. /* *
  34530. *
  34531. * (c) 2010-2019 Torstein Honsi
  34532. *
  34533. * License: www.highcharts.com/license
  34534. *
  34535. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34536. *
  34537. * */
  34538. var pick = U.pick;
  34539. var seriesType = H.seriesType;
  34540. /**
  34541. * Spline series type.
  34542. *
  34543. * @private
  34544. * @class
  34545. * @name Highcharts.seriesTypes.spline
  34546. *
  34547. * @augments Highcarts.Series
  34548. */
  34549. seriesType('spline', 'line',
  34550. /**
  34551. * A spline series is a special type of line series, where the segments
  34552. * between the data points are smoothed.
  34553. *
  34554. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  34555. * Spline chart
  34556. * @sample {highstock} stock/demo/spline/
  34557. * Spline chart
  34558. *
  34559. * @extends plotOptions.series
  34560. * @excluding step
  34561. * @product highcharts highstock
  34562. * @optionparent plotOptions.spline
  34563. */
  34564. {},
  34565. /**
  34566. * @lends seriesTypes.spline.prototype
  34567. */
  34568. {
  34569. /* eslint-disable valid-jsdoc */
  34570. /**
  34571. * Get the spline segment from a given point's previous neighbour to the
  34572. * given point.
  34573. *
  34574. * @private
  34575. * @function Highcharts.seriesTypes.spline#getPointSpline
  34576. *
  34577. * @param {Array<Highcharts.Point>}
  34578. *
  34579. * @param {Highcharts.Point} point
  34580. *
  34581. * @param {number} i
  34582. *
  34583. * @return {Highcharts.SVGPathArray}
  34584. */
  34585. getPointSpline: function (points, point, i) {
  34586. var
  34587. // 1 means control points midway between points, 2 means 1/3
  34588. // from the point, 3 is 1/4 etc
  34589. smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1], nextPoint = points[i + 1], leftContX, leftContY, rightContX, rightContY, ret;
  34590. /**
  34591. * @private
  34592. */
  34593. function doCurve(otherPoint) {
  34594. return otherPoint &&
  34595. !otherPoint.isNull &&
  34596. otherPoint.doCurve !== false &&
  34597. // #6387, area splines next to null:
  34598. !point.isCliff;
  34599. }
  34600. // Find control points
  34601. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  34602. var lastX = lastPoint.plotX, lastY = lastPoint.plotY, nextX = nextPoint.plotX, nextY = nextPoint.plotY, correction = 0;
  34603. leftContX =
  34604. (smoothing * plotX + lastX) / denom;
  34605. leftContY =
  34606. (smoothing * plotY + lastY) / denom;
  34607. rightContX =
  34608. (smoothing * plotX + nextX) / denom;
  34609. rightContY =
  34610. (smoothing * plotY + nextY) / denom;
  34611. // Have the two control points make a straight line through main
  34612. // point
  34613. if (rightContX !== leftContX) { // #5016, division by zero
  34614. correction = (((rightContY - leftContY) *
  34615. (rightContX - plotX)) /
  34616. (rightContX - leftContX) + plotY - rightContY);
  34617. }
  34618. leftContY += correction;
  34619. rightContY += correction;
  34620. // to prevent false extremes, check that control points are
  34621. // between neighbouring points' y values
  34622. if (leftContY > lastY && leftContY > plotY) {
  34623. leftContY = Math.max(lastY, plotY);
  34624. // mirror of left control point
  34625. rightContY = 2 * plotY - leftContY;
  34626. }
  34627. else if (leftContY < lastY &&
  34628. leftContY < plotY) {
  34629. leftContY = Math.min(lastY, plotY);
  34630. rightContY = 2 * plotY - leftContY;
  34631. }
  34632. if (rightContY > nextY &&
  34633. rightContY > plotY) {
  34634. rightContY = Math.max(nextY, plotY);
  34635. leftContY = 2 * plotY - rightContY;
  34636. }
  34637. else if (rightContY < nextY &&
  34638. rightContY < plotY) {
  34639. rightContY = Math.min(nextY, plotY);
  34640. leftContY = 2 * plotY - rightContY;
  34641. }
  34642. // record for drawing in next point
  34643. point.rightContX = rightContX;
  34644. point.rightContY = rightContY;
  34645. }
  34646. // Visualize control points for debugging
  34647. /*
  34648. if (leftContX) {
  34649. this.chart.renderer.circle(
  34650. leftContX + this.chart.plotLeft,
  34651. leftContY + this.chart.plotTop,
  34652. 2
  34653. )
  34654. .attr({
  34655. stroke: 'red',
  34656. 'stroke-width': 2,
  34657. fill: 'none',
  34658. zIndex: 9
  34659. })
  34660. .add();
  34661. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  34662. leftContY + this.chart.plotTop,
  34663. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  34664. .attr({
  34665. stroke: 'red',
  34666. 'stroke-width': 2,
  34667. zIndex: 9
  34668. })
  34669. .add();
  34670. }
  34671. if (rightContX) {
  34672. this.chart.renderer.circle(
  34673. rightContX + this.chart.plotLeft,
  34674. rightContY + this.chart.plotTop,
  34675. 2
  34676. )
  34677. .attr({
  34678. stroke: 'green',
  34679. 'stroke-width': 2,
  34680. fill: 'none',
  34681. zIndex: 9
  34682. })
  34683. .add();
  34684. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  34685. rightContY + this.chart.plotTop,
  34686. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  34687. .attr({
  34688. stroke: 'green',
  34689. 'stroke-width': 2,
  34690. zIndex: 9
  34691. })
  34692. .add();
  34693. }
  34694. // */
  34695. ret = [
  34696. 'C',
  34697. pick(lastPoint.rightContX, lastPoint.plotX),
  34698. pick(lastPoint.rightContY, lastPoint.plotY),
  34699. pick(leftContX, plotX),
  34700. pick(leftContY, plotY),
  34701. plotX,
  34702. plotY
  34703. ];
  34704. // reset for updating series later
  34705. lastPoint.rightContX = lastPoint.rightContY = null;
  34706. return ret;
  34707. }
  34708. /* eslint-enable valid-jsdoc */
  34709. });
  34710. /**
  34711. * A `spline` series. If the [type](#series.spline.type) option is
  34712. * not specified, it is inherited from [chart.type](#chart.type).
  34713. *
  34714. * @extends series,plotOptions.spline
  34715. * @excluding dataParser, dataURL, step
  34716. * @product highcharts highstock
  34717. * @apioption series.spline
  34718. */
  34719. /**
  34720. * An array of data points for the series. For the `spline` series type,
  34721. * points can be given in the following ways:
  34722. *
  34723. * 1. An array of numerical values. In this case, the numerical values will be
  34724. * interpreted as `y` options. The `x` values will be automatically
  34725. * calculated, either starting at 0 and incremented by 1, or from
  34726. * `pointStart` and `pointInterval` given in the series options. If the axis
  34727. * has categories, these will be used. Example:
  34728. * ```js
  34729. * data: [0, 5, 3, 5]
  34730. * ```
  34731. *
  34732. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34733. * `x,y`. If the first value is a string, it is applied as the name of the
  34734. * point, and the `x` value is inferred.
  34735. * ```js
  34736. * data: [
  34737. * [0, 9],
  34738. * [1, 2],
  34739. * [2, 8]
  34740. * ]
  34741. * ```
  34742. *
  34743. * 3. An array of objects with named values. The following snippet shows only a
  34744. * few settings, see the complete options set below. If the total number of
  34745. * data points exceeds the series'
  34746. * [turboThreshold](#series.spline.turboThreshold),
  34747. * this option is not available.
  34748. * ```js
  34749. * data: [{
  34750. * x: 1,
  34751. * y: 9,
  34752. * name: "Point2",
  34753. * color: "#00FF00"
  34754. * }, {
  34755. * x: 1,
  34756. * y: 0,
  34757. * name: "Point1",
  34758. * color: "#FF00FF"
  34759. * }]
  34760. * ```
  34761. *
  34762. * @sample {highcharts} highcharts/chart/reflow-true/
  34763. * Numerical values
  34764. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34765. * Arrays of numeric x and y
  34766. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34767. * Arrays of datetime x and y
  34768. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34769. * Arrays of point.name and y
  34770. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34771. * Config objects
  34772. *
  34773. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  34774. * @extends series.line.data
  34775. * @product highcharts highstock
  34776. * @apioption series.spline.data
  34777. */
  34778. ''; // adds doclets above intro transpilat
  34779. });
  34780. _registerModule(_modules, 'parts/AreaSplineSeries.js', [_modules['parts/Globals.js']], function (H) {
  34781. /* *
  34782. *
  34783. * (c) 2010-2019 Torstein Honsi
  34784. *
  34785. * License: www.highcharts.com/license
  34786. *
  34787. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34788. *
  34789. * */
  34790. var areaProto = H.seriesTypes.area.prototype, defaultPlotOptions = H.defaultPlotOptions, LegendSymbolMixin = H.LegendSymbolMixin, seriesType = H.seriesType;
  34791. /**
  34792. * AreaSpline series type.
  34793. *
  34794. * @private
  34795. * @class
  34796. * @name Highcharts.seriesTypes.areaspline
  34797. *
  34798. * @augments Highcharts.Series
  34799. */
  34800. seriesType('areaspline', 'spline',
  34801. /**
  34802. * The area spline series is an area series where the graph between the
  34803. * points is smoothed into a spline.
  34804. *
  34805. * @sample {highcharts} highcharts/demo/areaspline/
  34806. * Area spline chart
  34807. * @sample {highstock} stock/demo/areaspline/
  34808. * Area spline chart
  34809. *
  34810. * @extends plotOptions.area
  34811. * @excluding step
  34812. * @product highcharts highstock
  34813. * @apioption plotOptions.areaspline
  34814. */
  34815. defaultPlotOptions.area, {
  34816. getStackPoints: areaProto.getStackPoints,
  34817. getGraphPath: areaProto.getGraphPath,
  34818. drawGraph: areaProto.drawGraph,
  34819. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  34820. });
  34821. /**
  34822. * A `areaspline` series. If the [type](#series.areaspline.type) option
  34823. * is not specified, it is inherited from [chart.type](#chart.type).
  34824. *
  34825. *
  34826. * @extends series,plotOptions.areaspline
  34827. * @excluding dataParser, dataURL
  34828. * @product highcharts highstock
  34829. * @apioption series.areaspline
  34830. */
  34831. /**
  34832. * An array of data points for the series. For the `areaspline` series
  34833. * type, points can be given in the following ways:
  34834. *
  34835. * 1. An array of numerical values. In this case, the numerical values will be
  34836. * interpreted as `y` options. The `x` values will be automatically
  34837. * calculated, either starting at 0 and incremented by 1, or from
  34838. * `pointStart` and `pointInterval` given in the series options. If the axis
  34839. * has categories, these will be used. Example:
  34840. * ```js
  34841. * data: [0, 5, 3, 5]
  34842. * ```
  34843. *
  34844. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34845. * `x,y`. If the first value is a string, it is applied as the name of the
  34846. * point, and the `x` value is inferred.
  34847. * ```js
  34848. * data: [
  34849. * [0, 10],
  34850. * [1, 9],
  34851. * [2, 3]
  34852. * ]
  34853. * ```
  34854. *
  34855. * 3. An array of objects with named values. The following snippet shows only a
  34856. * few settings, see the complete options set below. If the total number of
  34857. * data points exceeds the series'
  34858. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  34859. * available.
  34860. * ```js
  34861. * data: [{
  34862. * x: 1,
  34863. * y: 4,
  34864. * name: "Point2",
  34865. * color: "#00FF00"
  34866. * }, {
  34867. * x: 1,
  34868. * y: 4,
  34869. * name: "Point1",
  34870. * color: "#FF00FF"
  34871. * }]
  34872. * ```
  34873. *
  34874. * @sample {highcharts} highcharts/chart/reflow-true/
  34875. * Numerical values
  34876. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34877. * Arrays of numeric x and y
  34878. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34879. * Arrays of datetime x and y
  34880. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34881. * Arrays of point.name and y
  34882. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34883. * Config objects
  34884. *
  34885. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  34886. * @extends series.line.data
  34887. * @product highcharts highstock
  34888. * @apioption series.areaspline.data
  34889. */
  34890. ''; // adds doclets above into transpilat
  34891. });
  34892. _registerModule(_modules, 'parts/ColumnSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  34893. /* *
  34894. *
  34895. * (c) 2010-2019 Torstein Honsi
  34896. *
  34897. * License: www.highcharts.com/license
  34898. *
  34899. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34900. *
  34901. * */
  34902. /**
  34903. * Adjusted width and x offset of the columns for grouping.
  34904. *
  34905. * @private
  34906. * @interface Highcharts.ColumnMetricsObject
  34907. */ /**
  34908. * Width of the columns.
  34909. * @name Highcharts.ColumnMetricsObject#width
  34910. * @type {number}
  34911. */ /**
  34912. * Offset of the columns.
  34913. * @name Highcharts.ColumnMetricsObject#offset
  34914. * @type {number}
  34915. */
  34916. var animObject = U.animObject, clamp = U.clamp, defined = U.defined, extend = U.extend, isNumber = U.isNumber, pick = U.pick;
  34917. var color = H.color, LegendSymbolMixin = H.LegendSymbolMixin, merge = H.merge, noop = H.noop, Series = H.Series, seriesType = H.seriesType, svg = H.svg;
  34918. /**
  34919. * The column series type.
  34920. *
  34921. * @private
  34922. * @class
  34923. * @name Highcharts.seriesTypes.column
  34924. *
  34925. * @augments Highcharts.Series
  34926. */
  34927. seriesType('column', 'line',
  34928. /**
  34929. * Column series display one column per value along an X axis.
  34930. *
  34931. * @sample {highcharts} highcharts/demo/column-basic/
  34932. * Column chart
  34933. * @sample {highstock} stock/demo/column/
  34934. * Column chart
  34935. *
  34936. * @extends plotOptions.line
  34937. * @excluding connectNulls, dashStyle, gapSize, gapUnit, linecap,
  34938. * lineWidth, marker, connectEnds, step, useOhlcData
  34939. * @product highcharts highstock
  34940. * @optionparent plotOptions.column
  34941. */
  34942. {
  34943. /**
  34944. * The corner radius of the border surrounding each column or bar.
  34945. *
  34946. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  34947. * Rounded columns
  34948. *
  34949. * @product highcharts highstock gantt
  34950. *
  34951. * @private
  34952. */
  34953. borderRadius: 0,
  34954. /**
  34955. * When using automatic point colors pulled from the global
  34956. * [colors](colors) or series-specific
  34957. * [plotOptions.column.colors](series.colors) collections, this option
  34958. * determines whether the chart should receive one color per series or
  34959. * one color per point.
  34960. *
  34961. * In styled mode, the `colors` or `series.colors` arrays are not
  34962. * supported, and instead this option gives the points individual color
  34963. * class names on the form `highcharts-color-{n}`.
  34964. *
  34965. * @see [series colors](#plotOptions.column.colors)
  34966. *
  34967. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  34968. * False by default
  34969. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  34970. * True
  34971. *
  34972. * @type {boolean}
  34973. * @default false
  34974. * @since 2.0
  34975. * @product highcharts highstock gantt
  34976. * @apioption plotOptions.column.colorByPoint
  34977. */
  34978. /**
  34979. * A series specific or series type specific color set to apply instead
  34980. * of the global [colors](#colors) when [colorByPoint](
  34981. * #plotOptions.column.colorByPoint) is true.
  34982. *
  34983. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  34984. * @since 3.0
  34985. * @product highcharts highstock gantt
  34986. * @apioption plotOptions.column.colors
  34987. */
  34988. /**
  34989. * When true, each column edge is rounded to its nearest pixel in order
  34990. * to render sharp on screen. In some cases, when there are a lot of
  34991. * densely packed columns, this leads to visible difference in column
  34992. * widths or distance between columns. In these cases, setting `crisp`
  34993. * to `false` may look better, even though each column is rendered
  34994. * blurry.
  34995. *
  34996. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  34997. * Crisp is false
  34998. *
  34999. * @since 5.0.10
  35000. * @product highcharts highstock gantt
  35001. *
  35002. * @private
  35003. */
  35004. crisp: true,
  35005. /**
  35006. * Padding between each value groups, in x axis units.
  35007. *
  35008. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  35009. * 0.2 by default
  35010. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  35011. * No group padding - all columns are evenly spaced
  35012. *
  35013. * @product highcharts highstock gantt
  35014. *
  35015. * @private
  35016. */
  35017. groupPadding: 0.2,
  35018. /**
  35019. * Whether to group non-stacked columns or to let them render
  35020. * independent of each other. Non-grouped columns will be laid out
  35021. * individually and overlap each other.
  35022. *
  35023. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  35024. * Grouping disabled
  35025. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  35026. * Grouping disabled
  35027. *
  35028. * @type {boolean}
  35029. * @default true
  35030. * @since 2.3.0
  35031. * @product highcharts highstock gantt
  35032. * @apioption plotOptions.column.grouping
  35033. */
  35034. /**
  35035. * @ignore-option
  35036. * @private
  35037. */
  35038. marker: null,
  35039. /**
  35040. * The maximum allowed pixel width for a column, translated to the
  35041. * height of a bar in a bar chart. This prevents the columns from
  35042. * becoming too wide when there is a small number of points in the
  35043. * chart.
  35044. *
  35045. * @see [pointWidth](#plotOptions.column.pointWidth)
  35046. *
  35047. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  35048. * Limited to 50
  35049. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  35050. * Limited to 50
  35051. *
  35052. * @type {number}
  35053. * @since 4.1.8
  35054. * @product highcharts highstock gantt
  35055. * @apioption plotOptions.column.maxPointWidth
  35056. */
  35057. /**
  35058. * Padding between each column or bar, in x axis units.
  35059. *
  35060. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  35061. * 0.1 by default
  35062. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  35063. * 0.25
  35064. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  35065. * 0 for tightly packed columns
  35066. *
  35067. * @product highcharts highstock gantt
  35068. *
  35069. * @private
  35070. */
  35071. pointPadding: 0.1,
  35072. /**
  35073. * A pixel value specifying a fixed width for each column or bar. When
  35074. * `null`, the width is calculated from the `pointPadding` and
  35075. * `groupPadding`.
  35076. *
  35077. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  35078. *
  35079. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  35080. * 20px wide columns regardless of chart width or the amount of
  35081. * data points
  35082. *
  35083. * @type {number}
  35084. * @since 1.2.5
  35085. * @product highcharts highstock gantt
  35086. * @apioption plotOptions.column.pointWidth
  35087. */
  35088. /**
  35089. * A pixel value specifying a fixed width for the column or bar.
  35090. * Overrides pointWidth on the series.
  35091. *
  35092. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  35093. *
  35094. * @type {number}
  35095. * @default undefined
  35096. * @since 7.0.0
  35097. * @product highcharts highstock gantt
  35098. * @apioption series.column.data.pointWidth
  35099. */
  35100. /**
  35101. * The minimal height for a column or width for a bar. By default,
  35102. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  35103. * set the minimal point length to a pixel value like 3\. In stacked
  35104. * column charts, minPointLength might not be respected for tightly
  35105. * packed values.
  35106. *
  35107. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  35108. * Zero base value
  35109. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  35110. * Positive and negative close to zero values
  35111. *
  35112. * @product highcharts highstock gantt
  35113. *
  35114. * @private
  35115. */
  35116. minPointLength: 0,
  35117. /**
  35118. * When the series contains less points than the crop threshold, all
  35119. * points are drawn, event if the points fall outside the visible plot
  35120. * area at the current zoom. The advantage of drawing all points
  35121. * (including markers and columns), is that animation is performed on
  35122. * updates. On the other hand, when the series contains more points than
  35123. * the crop threshold, the series data is cropped to only contain points
  35124. * that fall within the plot area. The advantage of cropping away
  35125. * invisible points is to increase performance on large series.
  35126. *
  35127. * @product highcharts highstock gantt
  35128. *
  35129. * @private
  35130. */
  35131. cropThreshold: 50,
  35132. /**
  35133. * The X axis range that each point is valid for. This determines the
  35134. * width of the column. On a categorized axis, the range will be 1
  35135. * by default (one category unit). On linear and datetime axes, the
  35136. * range will be computed as the distance between the two closest data
  35137. * points.
  35138. *
  35139. * The default `null` means it is computed automatically, but this
  35140. * option can be used to override the automatic value.
  35141. *
  35142. * This option is set by default to 1 if data sorting is enabled.
  35143. *
  35144. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  35145. * Set the point range to one day on a data set with one week
  35146. * between the points
  35147. *
  35148. * @type {number|null}
  35149. * @since 2.3
  35150. * @product highcharts highstock gantt
  35151. *
  35152. * @private
  35153. */
  35154. pointRange: null,
  35155. states: {
  35156. /**
  35157. * Options for the hovered point. These settings override the normal
  35158. * state options when a point is moused over or touched.
  35159. *
  35160. * @extends plotOptions.series.states.hover
  35161. * @excluding halo, lineWidth, lineWidthPlus, marker
  35162. * @product highcharts highstock gantt
  35163. */
  35164. hover: {
  35165. /** @ignore-option */
  35166. halo: false,
  35167. /**
  35168. * A specific border color for the hovered point. Defaults to
  35169. * inherit the normal state border color.
  35170. *
  35171. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35172. * @product highcharts gantt
  35173. * @apioption plotOptions.column.states.hover.borderColor
  35174. */
  35175. /**
  35176. * A specific color for the hovered point.
  35177. *
  35178. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35179. * @product highcharts gantt
  35180. * @apioption plotOptions.column.states.hover.color
  35181. */
  35182. /**
  35183. * How much to brighten the point on interaction. Requires the
  35184. * main color to be defined in hex or rgb(a) format.
  35185. *
  35186. * In styled mode, the hover brightening is by default replaced
  35187. * with a fill-opacity set in the `.highcharts-point:hover`
  35188. * rule.
  35189. *
  35190. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  35191. * Brighten by 0.5
  35192. *
  35193. * @product highcharts highstock gantt
  35194. */
  35195. brightness: 0.1
  35196. },
  35197. /**
  35198. * Options for the selected point. These settings override the
  35199. * normal state options when a point is selected.
  35200. *
  35201. * @extends plotOptions.series.states.select
  35202. * @excluding halo, lineWidth, lineWidthPlus, marker
  35203. * @product highcharts highstock gantt
  35204. */
  35205. select: {
  35206. /**
  35207. * A specific color for the selected point.
  35208. *
  35209. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35210. * @default #cccccc
  35211. * @product highcharts highstock gantt
  35212. */
  35213. color: '#cccccc',
  35214. /**
  35215. * A specific border color for the selected point.
  35216. *
  35217. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35218. * @default #000000
  35219. * @product highcharts highstock gantt
  35220. */
  35221. borderColor: '#000000'
  35222. }
  35223. },
  35224. dataLabels: {
  35225. align: null,
  35226. verticalAlign: null,
  35227. y: null
  35228. },
  35229. /**
  35230. * When this is true, the series will not cause the Y axis to cross
  35231. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  35232. * unless the data actually crosses the plane.
  35233. *
  35234. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  35235. * 3 will make the Y axis show negative values according to the
  35236. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  35237. * at 0.
  35238. *
  35239. * @since 4.1.9
  35240. * @product highcharts highstock
  35241. *
  35242. * @private
  35243. */
  35244. softThreshold: false,
  35245. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  35246. /**
  35247. * @ignore-option
  35248. * @private
  35249. */
  35250. startFromThreshold: true,
  35251. stickyTracking: false,
  35252. tooltip: {
  35253. distance: 6
  35254. },
  35255. /**
  35256. * The Y axis value to serve as the base for the columns, for
  35257. * distinguishing between values above and below a threshold. If `null`,
  35258. * the columns extend from the padding Y axis minimum.
  35259. *
  35260. * @since 2.0
  35261. * @product highcharts
  35262. *
  35263. * @private
  35264. */
  35265. threshold: 0,
  35266. /**
  35267. * The width of the border surrounding each column or bar. Defaults to
  35268. * `1` when there is room for a border, but to `0` when the columns are
  35269. * so dense that a border would cover the next column.
  35270. *
  35271. * In styled mode, the stroke width can be set with the
  35272. * `.highcharts-point` rule.
  35273. *
  35274. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  35275. * 2px black border
  35276. *
  35277. * @type {number}
  35278. * @default undefined
  35279. * @product highcharts highstock gantt
  35280. * @apioption plotOptions.column.borderWidth
  35281. */
  35282. /**
  35283. * The color of the border surrounding each column or bar.
  35284. *
  35285. * In styled mode, the border stroke can be set with the
  35286. * `.highcharts-point` rule.
  35287. *
  35288. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  35289. * Dark gray border
  35290. *
  35291. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35292. * @default #ffffff
  35293. * @product highcharts highstock gantt
  35294. *
  35295. * @private
  35296. */
  35297. borderColor: '#ffffff'
  35298. },
  35299. /**
  35300. * @lends seriesTypes.column.prototype
  35301. */
  35302. {
  35303. cropShoulder: 0,
  35304. // When tooltip is not shared, this series (and derivatives) requires
  35305. // direct touch/hover. KD-tree does not apply.
  35306. directTouch: true,
  35307. trackerGroups: ['group', 'dataLabelsGroup'],
  35308. // use separate negative stacks, unlike area stacks where a negative
  35309. // point is substracted from previous (#1910)
  35310. negStacks: true,
  35311. /* eslint-disable valid-jsdoc */
  35312. /**
  35313. * Initialize the series. Extends the basic Series.init method by
  35314. * marking other series of the same type as dirty.
  35315. *
  35316. * @private
  35317. * @function Highcharts.seriesTypes.column#init
  35318. * @return {void}
  35319. */
  35320. init: function () {
  35321. Series.prototype.init.apply(this, arguments);
  35322. var series = this, chart = series.chart;
  35323. // if the series is added dynamically, force redraw of other
  35324. // series affected by a new column
  35325. if (chart.hasRendered) {
  35326. chart.series.forEach(function (otherSeries) {
  35327. if (otherSeries.type === series.type) {
  35328. otherSeries.isDirty = true;
  35329. }
  35330. });
  35331. }
  35332. },
  35333. /**
  35334. * Return the width and x offset of the columns adjusted for grouping,
  35335. * groupPadding, pointPadding, pointWidth etc.
  35336. *
  35337. * @private
  35338. * @function Highcharts.seriesTypes.column#getColumnMetrics
  35339. * @return {Highcharts.ColumnMetricsObject}
  35340. */
  35341. getColumnMetrics: function () {
  35342. var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks,
  35343. // Keep backward compatibility: reversed xAxis had reversed
  35344. // stacks
  35345. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  35346. (!xAxis.reversed && reversedStacks), stackKey, stackGroups = {}, columnCount = 0;
  35347. // Get the total number of column type series. This is called on
  35348. // every series. Consider moving this logic to a chart.orderStacks()
  35349. // function and call it on init, addSeries and removeSeries
  35350. if (options.grouping === false) {
  35351. columnCount = 1;
  35352. }
  35353. else {
  35354. series.chart.series.forEach(function (otherSeries) {
  35355. var otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options, columnIndex;
  35356. if (otherSeries.type === series.type &&
  35357. (otherSeries.visible ||
  35358. !series.chart.options.chart
  35359. .ignoreHiddenSeries) &&
  35360. yAxis.len === otherYAxis.len &&
  35361. yAxis.pos === otherYAxis.pos) { // #642, #2086
  35362. if (otherOptions.stacking) {
  35363. stackKey = otherSeries.stackKey;
  35364. if (typeof stackGroups[stackKey] ===
  35365. 'undefined') {
  35366. stackGroups[stackKey] = columnCount++;
  35367. }
  35368. columnIndex = stackGroups[stackKey];
  35369. }
  35370. else if (otherOptions.grouping !== false) { // #1162
  35371. columnIndex = columnCount++;
  35372. }
  35373. otherSeries.columnIndex = columnIndex;
  35374. }
  35375. });
  35376. }
  35377. var categoryWidth = Math.min(Math.abs(xAxis.transA) * (xAxis.ordinalSlope ||
  35378. options.pointRange ||
  35379. xAxis.closestPointRange ||
  35380. xAxis.tickInterval ||
  35381. 1), // #2610
  35382. xAxis.len // #1535
  35383. ), 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,
  35384. // #1251, #3737
  35385. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
  35386. (groupPadding +
  35387. colIndex * pointOffsetWidth -
  35388. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  35389. // Save it for reading in linked series (Error bars particularly)
  35390. series.columnMetrics = {
  35391. width: pointWidth,
  35392. offset: pointXOffset
  35393. };
  35394. return series.columnMetrics;
  35395. },
  35396. /**
  35397. * Make the columns crisp. The edges are rounded to the nearest full
  35398. * pixel.
  35399. *
  35400. * @private
  35401. * @function Highcharts.seriesTypes.column#crispCol
  35402. * @param {number} x
  35403. * @param {number} y
  35404. * @param {number} w
  35405. * @param {number} h
  35406. * @return {Highcharts.BBoxObject}
  35407. */
  35408. crispCol: function (x, y, w, h) {
  35409. var chart = this.chart, borderWidth = this.borderWidth, xCrisp = -(borderWidth % 2 ? 0.5 : 0), yCrisp = borderWidth % 2 ? 0.5 : 1, right, bottom, fromTop;
  35410. if (chart.inverted && chart.renderer.isVML) {
  35411. yCrisp += 1;
  35412. }
  35413. // Horizontal. We need to first compute the exact right edge, then
  35414. // round it and compute the width from there.
  35415. if (this.options.crisp) {
  35416. right = Math.round(x + w) + xCrisp;
  35417. x = Math.round(x) + xCrisp;
  35418. w = right - x;
  35419. }
  35420. // Vertical
  35421. bottom = Math.round(y + h) + yCrisp;
  35422. fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  35423. y = Math.round(y) + yCrisp;
  35424. h = bottom - y;
  35425. // Top edges are exceptions
  35426. if (fromTop && h) { // #5146
  35427. y -= 1;
  35428. h += 1;
  35429. }
  35430. return {
  35431. x: x,
  35432. y: y,
  35433. width: w,
  35434. height: h
  35435. };
  35436. },
  35437. /**
  35438. * Translate each point to the plot area coordinate system and find
  35439. * shape positions
  35440. *
  35441. * @private
  35442. * @function Highcharts.seriesTypes.column#translate
  35443. */
  35444. translate: function () {
  35445. var series = this, chart = series.chart, options = series.options, dense = series.dense =
  35446. series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
  35447. ), yAxis = series.yAxis, threshold = options.threshold, translatedThreshold = series.translatedThreshold =
  35448. yAxis.getThreshold(threshold), minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), seriesPointWidth = metrics.width,
  35449. // postprocessed for border width
  35450. seriesBarW = series.barW =
  35451. Math.max(seriesPointWidth, 1 + 2 * borderWidth), seriesXOffset = series.pointXOffset = metrics.offset, dataMin = series.dataMin, dataMax = series.dataMax;
  35452. if (chart.inverted) {
  35453. translatedThreshold -= 0.5; // #3355
  35454. }
  35455. // When the pointPadding is 0, we want the columns to be packed
  35456. // tightly, so we allow individual columns to have individual sizes.
  35457. // When pointPadding is greater, we strive for equal-width columns
  35458. // (#2694).
  35459. if (options.pointPadding) {
  35460. seriesBarW = Math.ceil(seriesBarW);
  35461. }
  35462. Series.prototype.translate.apply(series);
  35463. // Record the new values
  35464. series.points.forEach(function (point) {
  35465. var yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), pointWidth = seriesPointWidth,
  35466. // Don't draw too far outside plot area (#1303, #2241,
  35467. // #4264)
  35468. plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance), barX = point.plotX + seriesXOffset, barW = seriesBarW, barY = Math.min(plotY, yBottom), up, barH = Math.max(plotY, yBottom) - barY;
  35469. // Handle options.minPointLength
  35470. if (minPointLength && Math.abs(barH) < minPointLength) {
  35471. barH = minPointLength;
  35472. up = (!yAxis.reversed && !point.negative) ||
  35473. (yAxis.reversed && point.negative);
  35474. // Reverse zeros if there's no positive value in the series
  35475. // in visible range (#7046)
  35476. if (point.y === threshold &&
  35477. series.dataMax <= threshold &&
  35478. // and if there's room for it (#7311)
  35479. yAxis.min < threshold &&
  35480. // if all points are the same value (i.e zero) not draw
  35481. // as negative points (#10646)
  35482. dataMin !== dataMax) {
  35483. up = !up;
  35484. }
  35485. // If stacked...
  35486. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  35487. // ...keep position
  35488. yBottom - minPointLength :
  35489. // #1485, #4051
  35490. translatedThreshold -
  35491. (up ? minPointLength : 0));
  35492. }
  35493. // Handle point.options.pointWidth
  35494. // @todo Handle grouping/stacking too. Calculate offset properly
  35495. if (defined(point.options.pointWidth)) {
  35496. pointWidth = barW =
  35497. Math.ceil(point.options.pointWidth);
  35498. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  35499. }
  35500. // Cache for access in polar
  35501. point.barX = barX;
  35502. point.pointWidth = pointWidth;
  35503. // Fix the tooltip on center of grouped columns (#1216, #424,
  35504. // #3648)
  35505. point.tooltipPos = chart.inverted ?
  35506. [
  35507. yAxis.len + yAxis.pos - chart.plotLeft - plotY,
  35508. series.xAxis.len - barX - barW / 2, barH
  35509. ] :
  35510. [barX + barW / 2, plotY + yAxis.pos -
  35511. chart.plotTop, barH];
  35512. // Register shape type and arguments to be used in drawPoints
  35513. // Allow shapeType defined on pointClass level
  35514. point.shapeType =
  35515. series.pointClass.prototype.shapeType || 'rect';
  35516. point.shapeArgs = series.crispCol.apply(series, point.isNull ?
  35517. // #3169, drilldown from null must have a position to work
  35518. // from #6585, dataLabel should be placed on xAxis, not
  35519. // floating in the middle of the chart
  35520. [barX, translatedThreshold, barW, 0] :
  35521. [barX, barY, barW, barH]);
  35522. });
  35523. },
  35524. getSymbol: noop,
  35525. /**
  35526. * Use a solid rectangle like the area series types
  35527. *
  35528. * @private
  35529. * @function Highcharts.seriesTypes.column#drawLegendSymbol
  35530. *
  35531. * @param {Highcharts.Legend} legend
  35532. * The legend object
  35533. *
  35534. * @param {Highcharts.Series|Highcharts.Point} item
  35535. * The series (this) or point
  35536. */
  35537. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  35538. /**
  35539. * Columns have no graph
  35540. *
  35541. * @private
  35542. * @function Highcharts.seriesTypes.column#drawGraph
  35543. */
  35544. drawGraph: function () {
  35545. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  35546. },
  35547. /**
  35548. * Get presentational attributes
  35549. *
  35550. * @private
  35551. * @function Highcharts.seriesTypes.column#pointAttribs
  35552. *
  35553. * @param {Highcharts.ColumnPoint} point
  35554. *
  35555. * @param {string} state
  35556. *
  35557. * @return {Highcharts.SVGAttributes}
  35558. */
  35559. pointAttribs: function (point, state) {
  35560. var options = this.options, stateOptions, ret, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth', fill = (point && point.color) || this.color,
  35561. // set to fill when borderColor null:
  35562. stroke = ((point && point[strokeOption]) ||
  35563. options[strokeOption] ||
  35564. this.color ||
  35565. fill), strokeWidth = (point && point[strokeWidthOption]) ||
  35566. options[strokeWidthOption] ||
  35567. this[strokeWidthOption] || 0, dashstyle = (point && point.options.dashStyle) || options.dashStyle, opacity = pick(point && point.opacity, options.opacity, 1), zone, brightness;
  35568. // Handle zone colors
  35569. if (point && this.zones.length) {
  35570. zone = point.getZone();
  35571. // When zones are present, don't use point.color (#4267).
  35572. // Changed order (#6527), added support for colorAxis (#10670)
  35573. fill = (point.options.color ||
  35574. (zone && (zone.color || point.nonZonedColor)) ||
  35575. this.color);
  35576. if (zone) {
  35577. stroke = zone.borderColor || stroke;
  35578. dashstyle = zone.dashStyle || dashstyle;
  35579. strokeWidth = zone.borderWidth || strokeWidth;
  35580. }
  35581. }
  35582. // Select or hover states
  35583. if (state && point) {
  35584. stateOptions = merge(options.states[state],
  35585. // #6401
  35586. point.options.states &&
  35587. point.options.states[state] ||
  35588. {});
  35589. brightness = stateOptions.brightness;
  35590. fill =
  35591. stateOptions.color || (typeof brightness !== 'undefined' &&
  35592. color(fill)
  35593. .brighten(stateOptions.brightness)
  35594. .get()) || fill;
  35595. stroke = stateOptions[strokeOption] || stroke;
  35596. strokeWidth =
  35597. stateOptions[strokeWidthOption] || strokeWidth;
  35598. dashstyle = stateOptions.dashStyle || dashstyle;
  35599. opacity = pick(stateOptions.opacity, opacity);
  35600. }
  35601. ret = {
  35602. fill: fill,
  35603. stroke: stroke,
  35604. 'stroke-width': strokeWidth,
  35605. opacity: opacity
  35606. };
  35607. if (dashstyle) {
  35608. ret.dashstyle = dashstyle;
  35609. }
  35610. return ret;
  35611. },
  35612. /**
  35613. * Draw the columns. For bars, the series.group is rotated, so the same
  35614. * coordinates apply for columns and bars. This method is inherited by
  35615. * scatter series.
  35616. *
  35617. * @private
  35618. * @function Highcharts.seriesTypes.column#drawPoints
  35619. */
  35620. drawPoints: function () {
  35621. var series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250, shapeArgs;
  35622. // draw the columns
  35623. series.points.forEach(function (point) {
  35624. var plotY = point.plotY, graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ?
  35625. 'animate' : 'attr';
  35626. if (isNumber(plotY) && point.y !== null) {
  35627. shapeArgs = point.shapeArgs;
  35628. // When updating a series between 2d and 3d or cartesian and
  35629. // polar, the shape type changes.
  35630. if (graphic && point.hasNewShapeType()) {
  35631. graphic = graphic.destroy();
  35632. }
  35633. // Set starting position for point sliding animation.
  35634. if (series.enabledDataSorting) {
  35635. point.startXPos = series.xAxis.reversed ?
  35636. -(shapeArgs ? shapeArgs.width : 0) :
  35637. series.xAxis.width;
  35638. }
  35639. if (!graphic) {
  35640. point.graphic = graphic =
  35641. renderer[point.shapeType](shapeArgs)
  35642. .add(point.group || series.group);
  35643. if (graphic &&
  35644. series.enabledDataSorting &&
  35645. chart.hasRendered &&
  35646. chart.pointCount < animationLimit) {
  35647. graphic.attr({
  35648. x: point.startXPos
  35649. });
  35650. hasGraphic = true;
  35651. verb = 'animate';
  35652. }
  35653. }
  35654. if (graphic && hasGraphic) { // update
  35655. graphic[verb](merge(shapeArgs));
  35656. }
  35657. // Border radius is not stylable (#6900)
  35658. if (options.borderRadius) {
  35659. graphic[verb]({
  35660. r: options.borderRadius
  35661. });
  35662. }
  35663. // Presentational
  35664. if (!chart.styledMode) {
  35665. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  35666. .shadow(point.allowShadow !== false && options.shadow, null, options.stacking && !options.borderRadius);
  35667. }
  35668. graphic.addClass(point.getClassName(), true);
  35669. }
  35670. else if (graphic) {
  35671. point.graphic = graphic.destroy(); // #1269
  35672. }
  35673. });
  35674. },
  35675. /**
  35676. * Animate the column heights one by one from zero.
  35677. *
  35678. * @private
  35679. * @function Highcharts.seriesTypes.column#animate
  35680. *
  35681. * @param {boolean} init
  35682. * Whether to initialize the animation or run it
  35683. */
  35684. animate: function (init) {
  35685. var series = this, yAxis = this.yAxis, options = series.options, inverted = this.chart.inverted, attr = {}, translateProp = inverted ? 'translateX' : 'translateY', translateStart, translatedThreshold;
  35686. if (svg) { // VML is too slow anyway
  35687. if (init) {
  35688. attr.scaleY = 0.001;
  35689. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxis.pos, yAxis.pos + yAxis.len);
  35690. if (inverted) {
  35691. attr.translateX = translatedThreshold - yAxis.len;
  35692. }
  35693. else {
  35694. attr.translateY = translatedThreshold;
  35695. }
  35696. // apply finnal clipping (used in Highstock) (#7083)
  35697. // animation is done by scaleY, so cliping is for panes
  35698. if (series.clipBox) {
  35699. series.setClip();
  35700. }
  35701. series.group.attr(attr);
  35702. }
  35703. else { // run the animation
  35704. translateStart = series.group.attr(translateProp);
  35705. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  35706. // Do the scale synchronously to ensure smooth
  35707. // updating (#5030, #7228)
  35708. step: function (val, fx) {
  35709. attr[translateProp] =
  35710. translateStart +
  35711. fx.pos * (yAxis.pos - translateStart);
  35712. series.group.attr(attr);
  35713. }
  35714. }));
  35715. // delete this function to allow it only once
  35716. series.animate = null;
  35717. }
  35718. }
  35719. },
  35720. /**
  35721. * Remove this series from the chart
  35722. *
  35723. * @private
  35724. * @function Highcharts.seriesTypes.column#remove
  35725. */
  35726. remove: function () {
  35727. var series = this, chart = series.chart;
  35728. // column and bar series affects other series of the same type
  35729. // as they are either stacked or grouped
  35730. if (chart.hasRendered) {
  35731. chart.series.forEach(function (otherSeries) {
  35732. if (otherSeries.type === series.type) {
  35733. otherSeries.isDirty = true;
  35734. }
  35735. });
  35736. }
  35737. Series.prototype.remove.apply(series, arguments);
  35738. }
  35739. });
  35740. /* eslint-enable valid-jsdoc */
  35741. /**
  35742. * A `column` series. If the [type](#series.column.type) option is
  35743. * not specified, it is inherited from [chart.type](#chart.type).
  35744. *
  35745. * @extends series,plotOptions.column
  35746. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  35747. * lineWidth, marker, connectEnds, step
  35748. * @product highcharts highstock
  35749. * @apioption series.column
  35750. */
  35751. /**
  35752. * An array of data points for the series. For the `column` series type,
  35753. * points can be given in the following ways:
  35754. *
  35755. * 1. An array of numerical values. In this case, the numerical values will be
  35756. * interpreted as `y` options. The `x` values will be automatically
  35757. * calculated, either starting at 0 and incremented by 1, or from
  35758. * `pointStart` and `pointInterval` given in the series options. If the axis
  35759. * has categories, these will be used. Example:
  35760. * ```js
  35761. * data: [0, 5, 3, 5]
  35762. * ```
  35763. *
  35764. * 2. An array of arrays with 2 values. In this case, the values correspond to
  35765. * `x,y`. If the first value is a string, it is applied as the name of the
  35766. * point, and the `x` value is inferred.
  35767. * ```js
  35768. * data: [
  35769. * [0, 6],
  35770. * [1, 2],
  35771. * [2, 6]
  35772. * ]
  35773. * ```
  35774. *
  35775. * 3. An array of objects with named values. The following snippet shows only a
  35776. * few settings, see the complete options set below. If the total number of
  35777. * data points exceeds the series'
  35778. * [turboThreshold](#series.column.turboThreshold), this option is not
  35779. * available.
  35780. * ```js
  35781. * data: [{
  35782. * x: 1,
  35783. * y: 9,
  35784. * name: "Point2",
  35785. * color: "#00FF00"
  35786. * }, {
  35787. * x: 1,
  35788. * y: 6,
  35789. * name: "Point1",
  35790. * color: "#FF00FF"
  35791. * }]
  35792. * ```
  35793. *
  35794. * @sample {highcharts} highcharts/chart/reflow-true/
  35795. * Numerical values
  35796. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  35797. * Arrays of numeric x and y
  35798. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  35799. * Arrays of datetime x and y
  35800. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  35801. * Arrays of point.name and y
  35802. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35803. * Config objects
  35804. *
  35805. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  35806. * @extends series.line.data
  35807. * @excluding marker
  35808. * @product highcharts highstock
  35809. * @apioption series.column.data
  35810. */
  35811. /**
  35812. * The color of the border surrounding the column or bar.
  35813. *
  35814. * In styled mode, the border stroke can be set with the `.highcharts-point`
  35815. * rule.
  35816. *
  35817. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  35818. * Dark gray border
  35819. *
  35820. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35821. * @product highcharts highstock
  35822. * @apioption series.column.data.borderColor
  35823. */
  35824. /**
  35825. * The width of the border surrounding the column or bar.
  35826. *
  35827. * In styled mode, the stroke width can be set with the `.highcharts-point`
  35828. * rule.
  35829. *
  35830. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  35831. * 2px black border
  35832. *
  35833. * @type {number}
  35834. * @product highcharts highstock
  35835. * @apioption series.column.data.borderWidth
  35836. */
  35837. /**
  35838. * A name for the dash style to use for the column or bar. Overrides
  35839. * dashStyle on the series.
  35840. *
  35841. * In styled mode, the stroke dash-array can be set with the same classes as
  35842. * listed under [data.color](#series.column.data.color).
  35843. *
  35844. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  35845. *
  35846. * @type {Highcharts.DashStyleValue}
  35847. * @apioption series.column.data.dashStyle
  35848. */
  35849. /**
  35850. * A pixel value specifying a fixed width for the column or bar. Overrides
  35851. * pointWidth on the series.
  35852. *
  35853. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  35854. *
  35855. * @type {number}
  35856. * @apioption series.column.data.pointWidth
  35857. */
  35858. /**
  35859. * @excluding halo, lineWidth, lineWidthPlus, marker
  35860. * @product highcharts highstock
  35861. * @apioption series.column.states.hover
  35862. */
  35863. /**
  35864. * @excluding halo, lineWidth, lineWidthPlus, marker
  35865. * @product highcharts highstock
  35866. * @apioption series.column.states.select
  35867. */
  35868. ''; // includes above doclets in transpilat
  35869. });
  35870. _registerModule(_modules, 'parts/BarSeries.js', [_modules['parts/Globals.js']], function (H) {
  35871. /* *
  35872. *
  35873. * (c) 2010-2019 Torstein Honsi
  35874. *
  35875. * License: www.highcharts.com/license
  35876. *
  35877. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  35878. *
  35879. * */
  35880. var seriesType = H.seriesType;
  35881. /**
  35882. * Bar series type.
  35883. *
  35884. * @private
  35885. * @class
  35886. * @name Highcharts.seriesTypes.bar
  35887. *
  35888. * @augments Highcharts.Series
  35889. */
  35890. seriesType('bar', 'column',
  35891. /**
  35892. * A bar series is a special type of column series where the columns are
  35893. * horizontal.
  35894. *
  35895. * @sample highcharts/demo/bar-basic/
  35896. * Bar chart
  35897. *
  35898. * @extends plotOptions.column
  35899. * @product highcharts
  35900. * @apioption plotOptions.bar
  35901. */
  35902. /**
  35903. * @ignore
  35904. */
  35905. null, {
  35906. inverted: true
  35907. });
  35908. /**
  35909. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  35910. * it is inherited from [chart.type](#chart.type).
  35911. *
  35912. * @extends series,plotOptions.bar
  35913. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  35914. * linecap, lineWidth, marker, connectEnds, step
  35915. * @product highcharts
  35916. * @apioption series.bar
  35917. */
  35918. /**
  35919. * An array of data points for the series. For the `bar` series type,
  35920. * points can be given in the following ways:
  35921. *
  35922. * 1. An array of numerical values. In this case, the numerical values will be
  35923. * interpreted as `y` options. The `x` values will be automatically
  35924. * calculated, either starting at 0 and incremented by 1, or from
  35925. * `pointStart` and `pointInterval` given in the series options. If the axis
  35926. * has categories, these will be used. Example:
  35927. * ```js
  35928. * data: [0, 5, 3, 5]
  35929. * ```
  35930. *
  35931. * 2. An array of arrays with 2 values. In this case, the values correspond to
  35932. * `x,y`. If the first value is a string, it is applied as the name of the
  35933. * point, and the `x` value is inferred.
  35934. * ```js
  35935. * data: [
  35936. * [0, 5],
  35937. * [1, 10],
  35938. * [2, 3]
  35939. * ]
  35940. * ```
  35941. *
  35942. * 3. An array of objects with named values. The following snippet shows only a
  35943. * few settings, see the complete options set below. If the total number of
  35944. * data points exceeds the series'
  35945. * [turboThreshold](#series.bar.turboThreshold), this option is not
  35946. * available.
  35947. * ```js
  35948. * data: [{
  35949. * x: 1,
  35950. * y: 1,
  35951. * name: "Point2",
  35952. * color: "#00FF00"
  35953. * }, {
  35954. * x: 1,
  35955. * y: 10,
  35956. * name: "Point1",
  35957. * color: "#FF00FF"
  35958. * }]
  35959. * ```
  35960. *
  35961. * @sample {highcharts} highcharts/chart/reflow-true/
  35962. * Numerical values
  35963. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  35964. * Arrays of numeric x and y
  35965. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  35966. * Arrays of datetime x and y
  35967. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  35968. * Arrays of point.name and y
  35969. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35970. * Config objects
  35971. *
  35972. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  35973. * @extends series.column.data
  35974. * @product highcharts
  35975. * @apioption series.bar.data
  35976. */
  35977. /**
  35978. * @excluding halo,lineWidth,lineWidthPlus,marker
  35979. * @product highcharts highstock
  35980. * @apioption series.bar.states.hover
  35981. */
  35982. /**
  35983. * @excluding halo,lineWidth,lineWidthPlus,marker
  35984. * @product highcharts highstock
  35985. * @apioption series.bar.states.select
  35986. */
  35987. ''; // gets doclets above into transpilat
  35988. });
  35989. _registerModule(_modules, 'parts/ScatterSeries.js', [_modules['parts/Globals.js']], function (H) {
  35990. /* *
  35991. *
  35992. * (c) 2010-2019 Torstein Honsi
  35993. *
  35994. * License: www.highcharts.com/license
  35995. *
  35996. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  35997. *
  35998. * */
  35999. var Series = H.Series, seriesType = H.seriesType;
  36000. /**
  36001. * Scatter series type.
  36002. *
  36003. * @private
  36004. * @class
  36005. * @name Highcharts.seriesTypes.scatter
  36006. *
  36007. * @augments Highcharts.Series
  36008. */
  36009. seriesType('scatter', 'line',
  36010. /**
  36011. * A scatter plot uses cartesian coordinates to display values for two
  36012. * variables for a set of data.
  36013. *
  36014. * @sample {highcharts} highcharts/demo/scatter/
  36015. * Scatter plot
  36016. *
  36017. * @extends plotOptions.line
  36018. * @excluding pointPlacement, shadow, useOhlcData
  36019. * @product highcharts highstock
  36020. * @optionparent plotOptions.scatter
  36021. */
  36022. {
  36023. /**
  36024. * The width of the line connecting the data points.
  36025. *
  36026. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  36027. * 0 by default
  36028. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  36029. * 1px
  36030. *
  36031. * @product highcharts highstock
  36032. */
  36033. lineWidth: 0,
  36034. findNearestPointBy: 'xy',
  36035. /**
  36036. * Apply a jitter effect for the rendered markers. When plotting
  36037. * discrete values, a little random noise may help telling the points
  36038. * apart. The jitter setting applies a random displacement of up to `n`
  36039. * axis units in either direction. So for example on a horizontal X
  36040. * axis, setting the `jitter.x` to 0.24 will render the point in a
  36041. * random position between 0.24 units to the left and 0.24 units to the
  36042. * right of the true axis position. On a category axis, setting it to
  36043. * 0.5 will fill up the bin and make the data appear continuous.
  36044. *
  36045. * When rendered on top of a box plot or a column series, a jitter value
  36046. * of 0.24 will correspond to the underlying series' default
  36047. * [groupPadding](
  36048. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  36049. * and [pointPadding](
  36050. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  36051. * settings.
  36052. *
  36053. * @sample {highcharts} highcharts/series-scatter/jitter
  36054. * Jitter on a scatter plot
  36055. *
  36056. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  36057. * Jittered scatter plot on top of a box plot
  36058. *
  36059. * @product highcharts highstock
  36060. * @since 7.0.2
  36061. */
  36062. jitter: {
  36063. /**
  36064. * The maximal X offset for the random jitter effect.
  36065. */
  36066. x: 0,
  36067. /**
  36068. * The maximal Y offset for the random jitter effect.
  36069. */
  36070. y: 0
  36071. },
  36072. marker: {
  36073. enabled: true // Overrides auto-enabling in line series (#3647)
  36074. },
  36075. /**
  36076. * Sticky tracking of mouse events. When true, the `mouseOut` event
  36077. * on a series isn't triggered until the mouse moves over another
  36078. * series, or out of the plot area. When false, the `mouseOut` event on
  36079. * a series is triggered when the mouse leaves the area around the
  36080. * series' graph or markers. This also implies the tooltip. When
  36081. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  36082. * will be hidden when moving the mouse between series.
  36083. *
  36084. * @type {boolean}
  36085. * @default false
  36086. * @product highcharts highstock
  36087. * @apioption plotOptions.scatter.stickyTracking
  36088. */
  36089. /**
  36090. * A configuration object for the tooltip rendering of each single
  36091. * series. Properties are inherited from [tooltip](#tooltip).
  36092. * Overridable properties are `headerFormat`, `pointFormat`,
  36093. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  36094. * series, in a scatter plot the series.name by default shows in the
  36095. * headerFormat and point.x and point.y in the pointFormat.
  36096. *
  36097. * @product highcharts highstock
  36098. */
  36099. tooltip: {
  36100. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  36101. '<span style="font-size: 10px"> {series.name}</span><br/>',
  36102. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  36103. }
  36104. // Prototype members
  36105. }, {
  36106. sorted: false,
  36107. requireSorting: false,
  36108. noSharedTooltip: true,
  36109. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  36110. takeOrdinalPosition: false,
  36111. /* eslint-disable valid-jsdoc */
  36112. /**
  36113. * @private
  36114. * @function Highcharts.seriesTypes.scatter#drawGraph
  36115. */
  36116. drawGraph: function () {
  36117. if (this.options.lineWidth) {
  36118. Series.prototype.drawGraph.call(this);
  36119. }
  36120. },
  36121. // Optionally add the jitter effect
  36122. applyJitter: function () {
  36123. var series = this, jitter = this.options.jitter, len = this.points.length;
  36124. /**
  36125. * Return a repeatable, pseudo-random number based on an integer
  36126. * seed.
  36127. * @private
  36128. */
  36129. function unrandom(seed) {
  36130. var rand = Math.sin(seed) * 10000;
  36131. return rand - Math.floor(rand);
  36132. }
  36133. if (jitter) {
  36134. this.points.forEach(function (point, i) {
  36135. ['x', 'y'].forEach(function (dim, j) {
  36136. var axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
  36137. if (jitter[dim] && !point.isNull) {
  36138. axis = series[dim + 'Axis'];
  36139. translatedJitter =
  36140. jitter[dim] * axis.transA;
  36141. if (axis && !axis.isLog) {
  36142. // Identify the outer bounds of the jitter range
  36143. min = Math.max(0, point[plotProp] - translatedJitter);
  36144. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  36145. // Find a random position within this range
  36146. point[plotProp] = min +
  36147. (max - min) * unrandom(i + j * len);
  36148. // Update clientX for the tooltip k-d-tree
  36149. if (dim === 'x') {
  36150. point.clientX = point.plotX;
  36151. }
  36152. }
  36153. }
  36154. });
  36155. });
  36156. }
  36157. }
  36158. /* eslint-enable valid-jsdoc */
  36159. });
  36160. /* eslint-disable no-invalid-this */
  36161. H.addEvent(Series, 'afterTranslate', function () {
  36162. if (this.applyJitter) {
  36163. this.applyJitter();
  36164. }
  36165. });
  36166. /* eslint-enable no-invalid-this */
  36167. /**
  36168. * A `scatter` series. If the [type](#series.scatter.type) option is
  36169. * not specified, it is inherited from [chart.type](#chart.type).
  36170. *
  36171. * @extends series,plotOptions.scatter
  36172. * @excluding dataParser, dataURL, useOhlcData
  36173. * @product highcharts highstock
  36174. * @apioption series.scatter
  36175. */
  36176. /**
  36177. * An array of data points for the series. For the `scatter` series
  36178. * type, points can be given in the following ways:
  36179. *
  36180. * 1. An array of numerical values. In this case, the numerical values will be
  36181. * interpreted as `y` options. The `x` values will be automatically
  36182. * calculated, either starting at 0 and incremented by 1, or from
  36183. * `pointStart` and `pointInterval` given in the series options. If the axis
  36184. * has categories, these will be used. Example:
  36185. * ```js
  36186. * data: [0, 5, 3, 5]
  36187. * ```
  36188. *
  36189. * 2. An array of arrays with 2 values. In this case, the values correspond to
  36190. * `x,y`. If the first value is a string, it is applied as the name of the
  36191. * point, and the `x` value is inferred.
  36192. * ```js
  36193. * data: [
  36194. * [0, 0],
  36195. * [1, 8],
  36196. * [2, 9]
  36197. * ]
  36198. * ```
  36199. *
  36200. * 3. An array of objects with named values. The following snippet shows only a
  36201. * few settings, see the complete options set below. If the total number of
  36202. * data points exceeds the series'
  36203. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  36204. * available.
  36205. * ```js
  36206. * data: [{
  36207. * x: 1,
  36208. * y: 2,
  36209. * name: "Point2",
  36210. * color: "#00FF00"
  36211. * }, {
  36212. * x: 1,
  36213. * y: 4,
  36214. * name: "Point1",
  36215. * color: "#FF00FF"
  36216. * }]
  36217. * ```
  36218. *
  36219. * @sample {highcharts} highcharts/chart/reflow-true/
  36220. * Numerical values
  36221. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  36222. * Arrays of numeric x and y
  36223. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  36224. * Arrays of datetime x and y
  36225. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  36226. * Arrays of point.name and y
  36227. * @sample {highcharts} highcharts/series/data-array-of-objects/
  36228. * Config objects
  36229. *
  36230. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  36231. * @extends series.line.data
  36232. * @product highcharts highstock
  36233. * @apioption series.scatter.data
  36234. */
  36235. ''; // adds doclets above to transpilat
  36236. });
  36237. _registerModule(_modules, 'mixins/centered-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  36238. /* *
  36239. *
  36240. * (c) 2010-2019 Torstein Honsi
  36241. *
  36242. * License: www.highcharts.com/license
  36243. *
  36244. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36245. *
  36246. * */
  36247. /**
  36248. * @private
  36249. * @interface Highcharts.RadianAngles
  36250. */ /**
  36251. * @name Highcharts.RadianAngles#end
  36252. * @type {number}
  36253. */ /**
  36254. * @name Highcharts.RadianAngles#start
  36255. * @type {number}
  36256. */
  36257. var isNumber = U.isNumber, pick = U.pick, relativeLength = U.relativeLength;
  36258. var deg2rad = H.deg2rad;
  36259. /* eslint-disable valid-jsdoc */
  36260. /**
  36261. * @private
  36262. * @mixin Highcharts.CenteredSeriesMixin
  36263. */
  36264. H.CenteredSeriesMixin = {
  36265. /**
  36266. * Get the center of the pie based on the size and center options relative
  36267. * to the plot area. Borrowed by the polar and gauge series types.
  36268. *
  36269. * @private
  36270. * @function Highcharts.CenteredSeriesMixin.getCenter
  36271. *
  36272. * @return {Array<number>}
  36273. */
  36274. getCenter: function () {
  36275. 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, positions = [
  36276. pick(centerOption[0], '50%'),
  36277. pick(centerOption[1], '50%'),
  36278. options.size || '100%',
  36279. options.innerSize || 0
  36280. ], smallestSize = Math.min(plotWidth, plotHeight), i, value;
  36281. for (i = 0; i < 4; ++i) {
  36282. value = positions[i];
  36283. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  36284. // i == 0: centerX, relative to width
  36285. // i == 1: centerY, relative to height
  36286. // i == 2: size, relative to smallestSize
  36287. // i == 3: innerSize, relative to size
  36288. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  36289. }
  36290. // innerSize cannot be larger than size (#3632)
  36291. if (positions[3] > positions[2]) {
  36292. positions[3] = positions[2];
  36293. }
  36294. return positions;
  36295. },
  36296. /**
  36297. * getStartAndEndRadians - Calculates start and end angles in radians.
  36298. * Used in series types such as pie and sunburst.
  36299. *
  36300. * @private
  36301. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  36302. *
  36303. * @param {number} [start]
  36304. * Start angle in degrees.
  36305. *
  36306. * @param {number} [end]
  36307. * Start angle in degrees.
  36308. *
  36309. * @return {Highcharts.RadianAngles}
  36310. * Returns an object containing start and end angles as radians.
  36311. */
  36312. getStartAndEndRadians: function (start, end) {
  36313. var startAngle = isNumber(start) ? start : 0, // must be a number
  36314. endAngle = ((isNumber(end) && // must be a number
  36315. end > startAngle && // must be larger than the start angle
  36316. // difference must be less than 360 degrees
  36317. (end - startAngle) < 360) ?
  36318. end :
  36319. startAngle + 360), correction = -90;
  36320. return {
  36321. start: deg2rad * (startAngle + correction),
  36322. end: deg2rad * (endAngle + correction)
  36323. };
  36324. }
  36325. };
  36326. });
  36327. _registerModule(_modules, 'parts/PieSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  36328. /* *
  36329. *
  36330. * (c) 2010-2019 Torstein Honsi
  36331. *
  36332. * License: www.highcharts.com/license
  36333. *
  36334. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36335. *
  36336. * */
  36337. var clamp = U.clamp, defined = U.defined, isNumber = U.isNumber, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation;
  36338. var addEvent = H.addEvent, CenteredSeriesMixin = H.CenteredSeriesMixin, getStartAndEndRadians = CenteredSeriesMixin.getStartAndEndRadians, LegendSymbolMixin = H.LegendSymbolMixin, merge = H.merge, noop = H.noop, Point = H.Point, Series = H.Series, seriesType = H.seriesType, seriesTypes = H.seriesTypes, fireEvent = H.fireEvent;
  36339. /**
  36340. * Pie series type.
  36341. *
  36342. * @private
  36343. * @class
  36344. * @name Highcharts.seriesTypes.pie
  36345. *
  36346. * @augments Highcharts.Series
  36347. */
  36348. seriesType('pie', 'line',
  36349. /**
  36350. * A pie chart is a circular graphic which is divided into slices to
  36351. * illustrate numerical proportion.
  36352. *
  36353. * @sample highcharts/demo/pie-basic/
  36354. * Pie chart
  36355. *
  36356. * @extends plotOptions.line
  36357. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  36358. * cropThreshold, dashStyle, dataSorting, dragDrop,
  36359. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  36360. * marker, negativeColor, pointInterval, pointIntervalUnit,
  36361. * pointPlacement, pointStart, softThreshold, stacking, step,
  36362. * threshold, turboThreshold, zoneAxis, zones
  36363. * @product highcharts
  36364. * @optionparent plotOptions.pie
  36365. */
  36366. {
  36367. /**
  36368. * @excluding legendItemClick
  36369. * @apioption plotOptions.pie.events
  36370. */
  36371. /**
  36372. * Fires when the checkbox next to the point name in the legend is
  36373. * clicked. One parameter, event, is passed to the function. The state
  36374. * of the checkbox is found by event.checked. The checked item is found
  36375. * by event.item. Return false to prevent the default action which is to
  36376. * toggle the select state of the series.
  36377. *
  36378. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  36379. * Alert checkbox status
  36380. *
  36381. * @type {Function}
  36382. * @since 1.2.0
  36383. * @product highcharts
  36384. * @context Highcharts.Point
  36385. * @apioption plotOptions.pie.events.checkboxClick
  36386. */
  36387. /**
  36388. * Fires when the legend item belonging to the pie point (slice) is
  36389. * clicked. The `this` keyword refers to the point itself. One
  36390. * parameter, `event`, is passed to the function, containing common
  36391. * event information. The default action is to toggle the visibility of
  36392. * the point. This can be prevented by calling `event.preventDefault()`.
  36393. *
  36394. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  36395. * Confirm toggle visibility
  36396. *
  36397. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  36398. * @since 1.2.0
  36399. * @product highcharts
  36400. * @apioption plotOptions.pie.point.events.legendItemClick
  36401. */
  36402. /**
  36403. * The center of the pie chart relative to the plot area. Can be
  36404. * percentages or pixel values. The default behaviour (as of 3.0) is to
  36405. * center the pie so that all slices and data labels are within the plot
  36406. * area. As a consequence, the pie may actually jump around in a chart
  36407. * with dynamic values, as the data labels move. In that case, the
  36408. * center should be explicitly set, for example to `["50%", "50%"]`.
  36409. *
  36410. * @sample {highcharts} highcharts/plotoptions/pie-center/
  36411. * Centered at 100, 100
  36412. *
  36413. * @type {Array<(number|string|null),(number|string|null)>}
  36414. * @default [null, null]
  36415. * @product highcharts
  36416. *
  36417. * @private
  36418. */
  36419. center: [null, null],
  36420. /**
  36421. * The color of the pie series. A pie series is represented as an empty
  36422. * circle if the total sum of its values is 0. Use this property to
  36423. * define the color of its border.
  36424. *
  36425. * In styled mode, the color can be defined by the
  36426. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  36427. * color can be set with the `.highcharts-series`,
  36428. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  36429. * `.highcharts-series-{n}` class, or individual classes given by the
  36430. * `className` option.
  36431. *
  36432. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  36433. * Empty pie series
  36434. *
  36435. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36436. * @default #cccccc
  36437. * @apioption plotOptions.pie.color
  36438. */
  36439. /**
  36440. * @product highcharts
  36441. *
  36442. * @private
  36443. */
  36444. clip: false,
  36445. /**
  36446. * @ignore-option
  36447. *
  36448. * @private
  36449. */
  36450. colorByPoint: true,
  36451. /**
  36452. * A series specific or series type specific color set to use instead
  36453. * of the global [colors](#colors).
  36454. *
  36455. * @sample {highcharts} highcharts/demo/pie-monochrome/
  36456. * Set default colors for all pies
  36457. *
  36458. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  36459. * @since 3.0
  36460. * @product highcharts
  36461. * @apioption plotOptions.pie.colors
  36462. */
  36463. /**
  36464. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  36465. * @extends plotOptions.series.dataLabels
  36466. * @excluding align, allowOverlap, staggerLines, step
  36467. * @private
  36468. */
  36469. dataLabels: {
  36470. /**
  36471. * Alignment method for data labels. Possible values are:
  36472. *
  36473. * - `toPlotEdges`: Each label touches the nearest vertical edge of
  36474. * the plot area.
  36475. *
  36476. * - `connectors`: Connectors have the same x position and the
  36477. * widest label of each half (left & right) touches the nearest
  36478. * vertical edge of the plot area.
  36479. *
  36480. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  36481. * alignTo: connectors
  36482. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  36483. * alignTo: plotEdges
  36484. *
  36485. * @type {string}
  36486. * @since 7.0.0
  36487. * @product highcharts
  36488. * @apioption plotOptions.pie.dataLabels.alignTo
  36489. */
  36490. allowOverlap: true,
  36491. /**
  36492. * The color of the line connecting the data label to the pie slice.
  36493. * The default color is the same as the point's color.
  36494. *
  36495. * In styled mode, the connector stroke is given in the
  36496. * `.highcharts-data-label-connector` class.
  36497. *
  36498. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  36499. * Blue connectors
  36500. * @sample {highcharts} highcharts/css/pie-point/
  36501. * Styled connectors
  36502. *
  36503. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36504. * @since 2.1
  36505. * @product highcharts
  36506. * @apioption plotOptions.pie.dataLabels.connectorColor
  36507. */
  36508. /**
  36509. * The distance from the data label to the connector. Note that
  36510. * data labels also have a default `padding`, so in order for the
  36511. * connector to touch the text, the `padding` must also be 0.
  36512. *
  36513. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  36514. * No padding
  36515. *
  36516. * @since 2.1
  36517. * @product highcharts
  36518. */
  36519. connectorPadding: 5,
  36520. /**
  36521. * Specifies the method that is used to generate the connector path.
  36522. * Highcharts provides 3 built-in connector shapes: `'fixedOffset'`
  36523. * (default), `'straight'` and `'crookedLine'`. Using
  36524. * `'crookedLine'` has the most sense (in most of the cases) when
  36525. * `'alignTo'` is set.
  36526. *
  36527. * Users can provide their own method by passing a function instead
  36528. * of a String. 3 arguments are passed to the callback:
  36529. *
  36530. * - Object that holds the information about the coordinates of the
  36531. * label (`x` & `y` properties) and how the label is located in
  36532. * relation to the pie (`alignment` property). `alignment` can by
  36533. * one of the following:
  36534. * `'left'` (pie on the left side of the data label),
  36535. * `'right'` (pie on the right side of the data label) or
  36536. * `'center'` (data label overlaps the pie).
  36537. *
  36538. * - Object that holds the information about the position of the
  36539. * connector. Its `touchingSliceAt` porperty tells the position
  36540. * of the place where the connector touches the slice.
  36541. *
  36542. * - Data label options
  36543. *
  36544. * The function has to return an SVG path definition in array form
  36545. * (see the example).
  36546. *
  36547. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-string/
  36548. * connectorShape is a String
  36549. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-function/
  36550. * connectorShape is a function
  36551. *
  36552. * @type {string|Function}
  36553. * @since 7.0.0
  36554. * @product highcharts
  36555. */
  36556. connectorShape: 'fixedOffset',
  36557. /**
  36558. * The width of the line connecting the data label to the pie slice.
  36559. *
  36560. * In styled mode, the connector stroke width is given in the
  36561. * `.highcharts-data-label-connector` class.
  36562. *
  36563. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  36564. * Disable the connector
  36565. * @sample {highcharts} highcharts/css/pie-point/
  36566. * Styled connectors
  36567. *
  36568. * @type {number}
  36569. * @default 1
  36570. * @since 2.1
  36571. * @product highcharts
  36572. * @apioption plotOptions.pie.dataLabels.connectorWidth
  36573. */
  36574. /**
  36575. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  36576. * far from the vertical plot edge the coonnector path should be
  36577. * crooked.
  36578. *
  36579. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  36580. * crookDistance set to 90%
  36581. *
  36582. * @since 7.0.0
  36583. * @product highcharts
  36584. */
  36585. crookDistance: '70%',
  36586. /**
  36587. * The distance of the data label from the pie's edge. Negative
  36588. * numbers put the data label on top of the pie slices. Can also be
  36589. * defined as a percentage of pie's radius. Connectors are only
  36590. * shown for data labels outside the pie.
  36591. *
  36592. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  36593. * Data labels on top of the pie
  36594. *
  36595. * @since 2.1
  36596. * @product highcharts
  36597. */
  36598. distance: 30,
  36599. enabled: true,
  36600. formatter: function () {
  36601. return this.point.isNull ? void 0 : this.point.name;
  36602. },
  36603. /**
  36604. * Whether to render the connector as a soft arc or a line with
  36605. * sharp break. Works only if `connectorShape` equals to
  36606. * `fixedOffset`.
  36607. *
  36608. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-true/
  36609. * Soft
  36610. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-false/
  36611. * Non soft
  36612. *
  36613. * @since 2.1.7
  36614. * @product highcharts
  36615. */
  36616. softConnector: true,
  36617. /**
  36618. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow
  36619. * Long labels truncated with an ellipsis
  36620. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow-wrap
  36621. * Long labels are wrapped
  36622. *
  36623. * @type {Highcharts.CSSObject}
  36624. * @apioption plotOptions.pie.dataLabels.style
  36625. */
  36626. x: 0
  36627. },
  36628. /**
  36629. * If the total sum of the pie's values is 0, the series is represented
  36630. * as an empty circle . The `fillColor` option defines the color of that
  36631. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  36632. * the border thickness.
  36633. *
  36634. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  36635. * Empty pie series
  36636. *
  36637. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36638. * @private
  36639. */
  36640. fillColor: void 0,
  36641. /**
  36642. * The end angle of the pie in degrees where 0 is top and 90 is right.
  36643. * Defaults to `startAngle` plus 360.
  36644. *
  36645. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  36646. * Semi-circle donut
  36647. *
  36648. * @type {number}
  36649. * @since 1.3.6
  36650. * @product highcharts
  36651. * @apioption plotOptions.pie.endAngle
  36652. */
  36653. /**
  36654. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  36655. * this option tells whether the series shall be redrawn as if the
  36656. * hidden point were `null`.
  36657. *
  36658. * The default value changed from `false` to `true` with Highcharts
  36659. * 3.0.
  36660. *
  36661. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  36662. * True, the hiddden point is ignored
  36663. *
  36664. * @since 2.3.0
  36665. * @product highcharts
  36666. *
  36667. * @private
  36668. */
  36669. ignoreHiddenPoint: true,
  36670. /**
  36671. * @ignore-option
  36672. *
  36673. * @private
  36674. */
  36675. inactiveOtherPoints: true,
  36676. /**
  36677. * The size of the inner diameter for the pie. A size greater than 0
  36678. * renders a donut chart. Can be a percentage or pixel value.
  36679. * Percentages are relative to the pie size. Pixel values are given as
  36680. * integers.
  36681. *
  36682. *
  36683. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  36684. * area, not the pie size.
  36685. *
  36686. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  36687. * 80px inner size
  36688. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  36689. * 50% of the plot area
  36690. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  36691. * 3D donut
  36692. *
  36693. * @type {number|string}
  36694. * @default 0
  36695. * @since 2.0
  36696. * @product highcharts
  36697. * @apioption plotOptions.pie.innerSize
  36698. */
  36699. /**
  36700. * @ignore-option
  36701. *
  36702. * @private
  36703. */
  36704. legendType: 'point',
  36705. /**
  36706. * @ignore-option
  36707. *
  36708. * @private
  36709. */
  36710. marker: null,
  36711. /**
  36712. * The minimum size for a pie in response to auto margins. The pie will
  36713. * try to shrink to make room for data labels in side the plot area,
  36714. * but only to this size.
  36715. *
  36716. * @type {number|string}
  36717. * @default 80
  36718. * @since 3.0
  36719. * @product highcharts
  36720. * @apioption plotOptions.pie.minSize
  36721. */
  36722. /**
  36723. * The diameter of the pie relative to the plot area. Can be a
  36724. * percentage or pixel value. Pixel values are given as integers. The
  36725. * default behaviour (as of 3.0) is to scale to the plot area and give
  36726. * room for data labels within the plot area.
  36727. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  36728. * default size calculation. As a consequence, the size of the pie may
  36729. * vary when points are updated and data labels more around. In that
  36730. * case it is best to set a fixed value, for example `"75%"`.
  36731. *
  36732. * @sample {highcharts} highcharts/plotoptions/pie-size/
  36733. * Smaller pie
  36734. *
  36735. * @type {number|string|null}
  36736. * @product highcharts
  36737. *
  36738. * @private
  36739. */
  36740. size: null,
  36741. /**
  36742. * Whether to display this particular series or series type in the
  36743. * legend. Since 2.1, pies are not shown in the legend by default.
  36744. *
  36745. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  36746. * One series in the legend, one hidden
  36747. *
  36748. * @product highcharts
  36749. *
  36750. * @private
  36751. */
  36752. showInLegend: false,
  36753. /**
  36754. * If a point is sliced, moved out from the center, how many pixels
  36755. * should it be moved?.
  36756. *
  36757. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  36758. * 20px offset
  36759. *
  36760. * @product highcharts
  36761. *
  36762. * @private
  36763. */
  36764. slicedOffset: 10,
  36765. /**
  36766. * The start angle of the pie slices in degrees where 0 is top and 90
  36767. * right.
  36768. *
  36769. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  36770. * Start from right
  36771. *
  36772. * @type {number}
  36773. * @default 0
  36774. * @since 2.3.4
  36775. * @product highcharts
  36776. * @apioption plotOptions.pie.startAngle
  36777. */
  36778. /**
  36779. * Sticky tracking of mouse events. When true, the `mouseOut` event
  36780. * on a series isn't triggered until the mouse moves over another
  36781. * series, or out of the plot area. When false, the `mouseOut` event on
  36782. * a series is triggered when the mouse leaves the area around the
  36783. * series' graph or markers. This also implies the tooltip. When
  36784. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  36785. * will be hidden when moving the mouse between series.
  36786. *
  36787. * @product highcharts
  36788. *
  36789. * @private
  36790. */
  36791. stickyTracking: false,
  36792. tooltip: {
  36793. followPointer: true
  36794. },
  36795. /**
  36796. * The color of the border surrounding each slice. When `null`, the
  36797. * border takes the same color as the slice fill. This can be used
  36798. * together with a `borderWidth` to fill drawing gaps created by
  36799. * antialiazing artefacts in borderless pies.
  36800. *
  36801. * In styled mode, the border stroke is given in the `.highcharts-point`
  36802. * class.
  36803. *
  36804. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  36805. * Black border
  36806. *
  36807. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36808. * @default #ffffff
  36809. * @product highcharts
  36810. *
  36811. * @private
  36812. */
  36813. borderColor: '#ffffff',
  36814. /**
  36815. * The width of the border surrounding each slice.
  36816. *
  36817. * When setting the border width to 0, there may be small gaps between
  36818. * the slices due to SVG antialiasing artefacts. To work around this,
  36819. * keep the border width at 0.5 or 1, but set the `borderColor` to
  36820. * `null` instead.
  36821. *
  36822. * In styled mode, the border stroke width is given in the
  36823. * `.highcharts-point` class.
  36824. *
  36825. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  36826. * 3px border
  36827. *
  36828. * @product highcharts
  36829. *
  36830. * @private
  36831. */
  36832. borderWidth: 1,
  36833. /**
  36834. * @ignore-options
  36835. * @private
  36836. */
  36837. lineWidth: void 0,
  36838. states: {
  36839. /**
  36840. * @extends plotOptions.series.states.hover
  36841. * @excluding marker, lineWidth, lineWidthPlus
  36842. * @product highcharts
  36843. */
  36844. hover: {
  36845. /**
  36846. * How much to brighten the point on interaction. Requires the
  36847. * main color to be defined in hex or rgb(a) format.
  36848. *
  36849. * In styled mode, the hover brightness is by default replaced
  36850. * by a fill-opacity given in the `.highcharts-point-hover`
  36851. * class.
  36852. *
  36853. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  36854. * Brightened by 0.5
  36855. *
  36856. * @product highcharts
  36857. */
  36858. brightness: 0.1
  36859. }
  36860. }
  36861. },
  36862. /* eslint-disable valid-jsdoc */
  36863. /**
  36864. * @lends seriesTypes.pie.prototype
  36865. */
  36866. {
  36867. isCartesian: false,
  36868. requireSorting: false,
  36869. directTouch: true,
  36870. noSharedTooltip: true,
  36871. trackerGroups: ['group', 'dataLabelsGroup'],
  36872. axisTypes: [],
  36873. pointAttribs: seriesTypes.column.prototype.pointAttribs,
  36874. /**
  36875. * Animate the pies in
  36876. *
  36877. * @private
  36878. * @function Highcharts.seriesTypes.pie#animate
  36879. *
  36880. * @param {boolean} [init=false]
  36881. */
  36882. animate: function (init) {
  36883. var series = this, points = series.points, startAngleRad = series.startAngleRad;
  36884. if (!init) {
  36885. points.forEach(function (point) {
  36886. var graphic = point.graphic, args = point.shapeArgs;
  36887. if (graphic && args) {
  36888. // start values
  36889. graphic.attr({
  36890. // animate from inner radius (#779)
  36891. r: pick(point.startR, (series.center && series.center[3] / 2)),
  36892. start: startAngleRad,
  36893. end: startAngleRad
  36894. });
  36895. // animate
  36896. graphic.animate({
  36897. r: args.r,
  36898. start: args.start,
  36899. end: args.end
  36900. }, series.options.animation);
  36901. }
  36902. });
  36903. // delete this function to allow it only once
  36904. series.animate = null;
  36905. }
  36906. },
  36907. // Define hasData function for non-cartesian series.
  36908. // Returns true if the series has points at all.
  36909. hasData: function () {
  36910. return !!this.processedXData.length; // != 0
  36911. },
  36912. /**
  36913. * Recompute total chart sum and update percentages of points.
  36914. *
  36915. * @private
  36916. * @function Highcharts.seriesTypes.pie#updateTotals
  36917. * @return {void}
  36918. */
  36919. updateTotals: function () {
  36920. var i, total = 0, points = this.points, len = points.length, point, ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  36921. // Get the total sum
  36922. for (i = 0; i < len; i++) {
  36923. point = points[i];
  36924. total += (ignoreHiddenPoint && !point.visible) ?
  36925. 0 :
  36926. point.isNull ?
  36927. 0 :
  36928. point.y;
  36929. }
  36930. this.total = total;
  36931. // Set each point's properties
  36932. for (i = 0; i < len; i++) {
  36933. point = points[i];
  36934. point.percentage =
  36935. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  36936. point.y / total * 100 :
  36937. 0;
  36938. point.total = total;
  36939. }
  36940. },
  36941. /**
  36942. * Extend the generatePoints method by adding total and percentage
  36943. * properties to each point
  36944. *
  36945. * @private
  36946. * @function Highcharts.seriesTypes.pie#generatePoints
  36947. * @return {void}
  36948. */
  36949. generatePoints: function () {
  36950. Series.prototype.generatePoints.call(this);
  36951. this.updateTotals();
  36952. },
  36953. /**
  36954. * Utility for getting the x value from a given y, used for
  36955. * anticollision logic in data labels. Added point for using specific
  36956. * points' label distance.
  36957. * @private
  36958. */
  36959. getX: function (y, left, point) {
  36960. var center = this.center,
  36961. // Variable pie has individual radius
  36962. radius = this.radii ?
  36963. this.radii[point.index] :
  36964. center[2] / 2, angle, x;
  36965. angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  36966. x = center[0] +
  36967. (left ? -1 : 1) *
  36968. (Math.cos(angle) * (radius + point.labelDistance)) +
  36969. (point.labelDistance > 0 ?
  36970. (left ? -1 : 1) * this.options.dataLabels.padding :
  36971. 0);
  36972. return x;
  36973. },
  36974. /**
  36975. * Do translation for pie slices
  36976. *
  36977. * @private
  36978. * @function Highcharts.seriesTypes.pie#translate
  36979. * @param {Array<number>} [positions]
  36980. * @return {void}
  36981. */
  36982. translate: function (positions) {
  36983. this.generatePoints();
  36984. var series = this, cumulative = 0, precision = 1000, // issue #172
  36985. 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,
  36986. points = series.points,
  36987. // the x component of the radius vector for a given point
  36988. radiusX, radiusY, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, i, len = points.length, point;
  36989. // Get positions - either an integer or a percentage string must be
  36990. // given. If positions are passed as a parameter, we're in a
  36991. // recursive loop for adjusting space for data labels.
  36992. if (!positions) {
  36993. series.center = positions = series.getCenter();
  36994. }
  36995. // Calculate the geometry for each point
  36996. for (i = 0; i < len; i++) {
  36997. point = points[i];
  36998. // set start and end angle
  36999. start = startAngleRad + (cumulative * circ);
  37000. if (!ignoreHiddenPoint || point.visible) {
  37001. cumulative += point.percentage / 100;
  37002. }
  37003. end = startAngleRad + (cumulative * circ);
  37004. // set the shape
  37005. point.shapeType = 'arc';
  37006. point.shapeArgs = {
  37007. x: positions[0],
  37008. y: positions[1],
  37009. r: positions[2] / 2,
  37010. innerR: positions[3] / 2,
  37011. start: Math.round(start * precision) / precision,
  37012. end: Math.round(end * precision) / precision
  37013. };
  37014. // Used for distance calculation for specific point.
  37015. point.labelDistance = pick((point.options.dataLabels &&
  37016. point.options.dataLabels.distance), labelDistance);
  37017. // Compute point.labelDistance if it's defined as percentage
  37018. // of slice radius (#8854)
  37019. point.labelDistance = relativeLength(point.labelDistance, point.shapeArgs.r);
  37020. // Saved for later dataLabels distance calculation.
  37021. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  37022. // The angle must stay within -90 and 270 (#2645)
  37023. angle = (end + start) / 2;
  37024. if (angle > 1.5 * Math.PI) {
  37025. angle -= 2 * Math.PI;
  37026. }
  37027. else if (angle < -Math.PI / 2) {
  37028. angle += 2 * Math.PI;
  37029. }
  37030. // Center for the sliced out slice
  37031. point.slicedTranslation = {
  37032. translateX: Math.round(Math.cos(angle) * slicedOffset),
  37033. translateY: Math.round(Math.sin(angle) * slicedOffset)
  37034. };
  37035. // set the anchor point for tooltips
  37036. radiusX = Math.cos(angle) * positions[2] / 2;
  37037. radiusY = Math.sin(angle) * positions[2] / 2;
  37038. point.tooltipPos = [
  37039. positions[0] + radiusX * 0.7,
  37040. positions[1] + radiusY * 0.7
  37041. ];
  37042. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  37043. 1 :
  37044. 0;
  37045. point.angle = angle;
  37046. // Set the anchor point for data labels. Use point.labelDistance
  37047. // instead of labelDistance // #1174
  37048. // finalConnectorOffset - not override connectorOffset value.
  37049. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  37050. point.labelPosition = {
  37051. natural: {
  37052. // initial position of the data label - it's utilized for
  37053. // finding the final position for the label
  37054. x: positions[0] + radiusX + Math.cos(angle) *
  37055. point.labelDistance,
  37056. y: positions[1] + radiusY + Math.sin(angle) *
  37057. point.labelDistance
  37058. },
  37059. 'final': {
  37060. // used for generating connector path -
  37061. // initialized later in drawDataLabels function
  37062. // x: undefined,
  37063. // y: undefined
  37064. },
  37065. // left - pie on the left side of the data label
  37066. // right - pie on the right side of the data label
  37067. // center - data label overlaps the pie
  37068. alignment: point.labelDistance < 0 ?
  37069. 'center' : point.half ? 'right' : 'left',
  37070. connectorPosition: {
  37071. breakAt: {
  37072. x: positions[0] + radiusX + Math.cos(angle) *
  37073. finalConnectorOffset,
  37074. y: positions[1] + radiusY + Math.sin(angle) *
  37075. finalConnectorOffset
  37076. },
  37077. touchingSliceAt: {
  37078. x: positions[0] + radiusX,
  37079. y: positions[1] + radiusY
  37080. }
  37081. }
  37082. };
  37083. }
  37084. fireEvent(series, 'afterTranslate');
  37085. },
  37086. /**
  37087. * Called internally to draw auxiliary graph in pie-like series in
  37088. * situtation when the default graph is not sufficient enough to present
  37089. * the data well. Auxiliary graph is saved in the same object as
  37090. * regular graph.
  37091. *
  37092. * @private
  37093. * @function Highcharts.seriesTypes.pie#drawEmpty
  37094. */
  37095. drawEmpty: function () {
  37096. var centerX, centerY, options = this.options;
  37097. // Draw auxiliary graph if there're no visible points.
  37098. if (this.total === 0) {
  37099. centerX = this.center[0];
  37100. centerY = this.center[1];
  37101. if (!this.graph) { // Auxiliary graph doesn't exist yet.
  37102. this.graph = this.chart.renderer.circle(centerX, centerY, 0)
  37103. .addClass('highcharts-graph')
  37104. .add(this.group);
  37105. }
  37106. this.graph.animate({
  37107. 'stroke-width': options.borderWidth,
  37108. cx: centerX,
  37109. cy: centerY,
  37110. r: this.center[2] / 2,
  37111. fill: options.fillColor || 'none',
  37112. stroke: options.color ||
  37113. '#cccccc'
  37114. });
  37115. }
  37116. else if (this.graph) { // Destroy the graph object.
  37117. this.graph = this.graph.destroy();
  37118. }
  37119. },
  37120. /**
  37121. * Draw the data points
  37122. *
  37123. * @private
  37124. * @function Highcharts.seriesTypes.pie#drawPoints
  37125. * @return {void}
  37126. */
  37127. redrawPoints: function () {
  37128. var series = this, chart = series.chart, renderer = chart.renderer, groupTranslation, graphic, pointAttr, shapeArgs, shadow = series.options.shadow;
  37129. this.drawEmpty();
  37130. if (shadow && !series.shadowGroup && !chart.styledMode) {
  37131. series.shadowGroup = renderer.g('shadow')
  37132. .attr({ zIndex: -1 })
  37133. .add(series.group);
  37134. }
  37135. // draw the slices
  37136. series.points.forEach(function (point) {
  37137. var animateTo = {};
  37138. graphic = point.graphic;
  37139. if (!point.isNull && graphic) {
  37140. shapeArgs = point.shapeArgs;
  37141. // If the point is sliced, use special translation, else use
  37142. // plot area translation
  37143. groupTranslation = point.getTranslate();
  37144. if (!chart.styledMode) {
  37145. // Put the shadow behind all points
  37146. var shadowGroup = point.shadowGroup;
  37147. if (shadow && !shadowGroup) {
  37148. shadowGroup = point.shadowGroup = renderer
  37149. .g('shadow')
  37150. .add(series.shadowGroup);
  37151. }
  37152. if (shadowGroup) {
  37153. shadowGroup.attr(groupTranslation);
  37154. }
  37155. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  37156. }
  37157. // Draw the slice
  37158. if (!point.delayedRendering) {
  37159. graphic
  37160. .setRadialReference(series.center);
  37161. if (!chart.styledMode) {
  37162. merge(true, animateTo, pointAttr);
  37163. }
  37164. merge(true, animateTo, shapeArgs, groupTranslation);
  37165. graphic.animate(animateTo);
  37166. }
  37167. else {
  37168. graphic
  37169. .setRadialReference(series.center)
  37170. .attr(shapeArgs)
  37171. .attr(groupTranslation);
  37172. if (!chart.styledMode) {
  37173. graphic
  37174. .attr(pointAttr)
  37175. .attr({ 'stroke-linejoin': 'round' })
  37176. .shadow(shadow, shadowGroup);
  37177. }
  37178. point.delayedRendering = false;
  37179. }
  37180. graphic.attr({
  37181. visibility: point.visible ? 'inherit' : 'hidden'
  37182. });
  37183. graphic.addClass(point.getClassName());
  37184. }
  37185. else if (graphic) {
  37186. point.graphic = graphic.destroy();
  37187. }
  37188. });
  37189. },
  37190. /**
  37191. * Slices in pie chart are initialized in DOM, but it's shapes and
  37192. * animations are normally run in `drawPoints()`.
  37193. * @private
  37194. */
  37195. drawPoints: function () {
  37196. var renderer = this.chart.renderer;
  37197. this.points.forEach(function (point) {
  37198. if (!point.graphic) {
  37199. point.graphic = renderer[point.shapeType](point.shapeArgs)
  37200. .add(point.series.group);
  37201. point.delayedRendering = true;
  37202. }
  37203. });
  37204. },
  37205. /**
  37206. * @private
  37207. * @deprecated
  37208. * @function Highcharts.seriesTypes.pie#searchPoint
  37209. */
  37210. searchPoint: noop,
  37211. /**
  37212. * Utility for sorting data labels
  37213. *
  37214. * @private
  37215. * @function Highcharts.seriesTypes.pie#sortByAngle
  37216. * @param {Array<Highcharts.Point>} points
  37217. * @param {number} sign
  37218. * @return {void}
  37219. */
  37220. sortByAngle: function (points, sign) {
  37221. points.sort(function (a, b) {
  37222. return ((typeof a.angle !== 'undefined') &&
  37223. (b.angle - a.angle) * sign);
  37224. });
  37225. },
  37226. /**
  37227. * Use a simple symbol from LegendSymbolMixin.
  37228. *
  37229. * @private
  37230. * @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.pie#drawLegendSymbol
  37231. */
  37232. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  37233. /**
  37234. * Use the getCenter method from drawLegendSymbol.
  37235. *
  37236. * @private
  37237. * @borrows Highcharts.CenteredSeriesMixin.getCenter as Highcharts.seriesTypes.pie#getCenter
  37238. */
  37239. getCenter: CenteredSeriesMixin.getCenter,
  37240. /**
  37241. * Pies don't have point marker symbols.
  37242. *
  37243. * @deprecated
  37244. * @private
  37245. * @function Highcharts.seriesTypes.pie#getSymbol
  37246. */
  37247. getSymbol: noop,
  37248. /**
  37249. * @private
  37250. * @type {null}
  37251. */
  37252. drawGraph: null
  37253. },
  37254. /**
  37255. * @lends seriesTypes.pie.prototype.pointClass.prototype
  37256. */
  37257. {
  37258. /**
  37259. * Initialize the pie slice
  37260. *
  37261. * @private
  37262. * @function Highcharts.seriesTypes.pie#pointClass#init
  37263. * @return {Highcharts.Point}
  37264. */
  37265. init: function () {
  37266. Point.prototype.init.apply(this, arguments);
  37267. var point = this, toggleSlice;
  37268. point.name = pick(point.name, 'Slice');
  37269. // add event listener for select
  37270. toggleSlice = function (e) {
  37271. point.slice(e.type === 'select');
  37272. };
  37273. addEvent(point, 'select', toggleSlice);
  37274. addEvent(point, 'unselect', toggleSlice);
  37275. return point;
  37276. },
  37277. /**
  37278. * Negative points are not valid (#1530, #3623, #5322)
  37279. *
  37280. * @private
  37281. * @function Highcharts.seriesTypes.pie#pointClass#isValid
  37282. * @return {boolean}
  37283. */
  37284. isValid: function () {
  37285. return isNumber(this.y) && this.y >= 0;
  37286. },
  37287. /**
  37288. * Toggle the visibility of the pie slice
  37289. *
  37290. * @private
  37291. * @function Highcharts.seriesTypes.pie#pointClass#setVisible
  37292. * @param {boolean} vis
  37293. * Whether to show the slice or not. If undefined, the visibility
  37294. * is toggled.
  37295. * @param {boolean} [redraw=false]
  37296. * @return {void}
  37297. */
  37298. setVisible: function (vis, redraw) {
  37299. var point = this, series = point.series, chart = series.chart, ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  37300. redraw = pick(redraw, ignoreHiddenPoint);
  37301. if (vis !== point.visible) {
  37302. // If called without an argument, toggle visibility
  37303. point.visible = point.options.visible = vis =
  37304. typeof vis === 'undefined' ? !point.visible : vis;
  37305. // update userOptions.data
  37306. series.options.data[series.data.indexOf(point)] =
  37307. point.options;
  37308. // Show and hide associated elements. This is performed
  37309. // regardless of redraw or not, because chart.redraw only
  37310. // handles full series.
  37311. ['graphic', 'dataLabel', 'connector', 'shadowGroup'].forEach(function (key) {
  37312. if (point[key]) {
  37313. point[key][vis ? 'show' : 'hide'](true);
  37314. }
  37315. });
  37316. if (point.legendItem) {
  37317. chart.legend.colorizeItem(point, vis);
  37318. }
  37319. // #4170, hide halo after hiding point
  37320. if (!vis && point.state === 'hover') {
  37321. point.setState('');
  37322. }
  37323. // Handle ignore hidden slices
  37324. if (ignoreHiddenPoint) {
  37325. series.isDirty = true;
  37326. }
  37327. if (redraw) {
  37328. chart.redraw();
  37329. }
  37330. }
  37331. },
  37332. /**
  37333. * Set or toggle whether the slice is cut out from the pie
  37334. *
  37335. * @private
  37336. * @function Highcharts.seriesTypes.pie#pointClass#slice
  37337. * @param {boolean} sliced
  37338. * When undefined, the slice state is toggled.
  37339. * @param {boolean} redraw
  37340. * Whether to redraw the chart. True by default.
  37341. * @param {boolean|Highcharts.AnimationOptionsObject}
  37342. * Animation options.
  37343. * @return {void}
  37344. */
  37345. slice: function (sliced, redraw, animation) {
  37346. var point = this, series = point.series, chart = series.chart;
  37347. setAnimation(animation, chart);
  37348. // redraw is true by default
  37349. redraw = pick(redraw, true);
  37350. /**
  37351. * Pie series only. Whether to display a slice offset from the
  37352. * center.
  37353. * @name Highcharts.Point#sliced
  37354. * @type {boolean|undefined}
  37355. */
  37356. // if called without an argument, toggle
  37357. point.sliced = point.options.sliced = sliced =
  37358. defined(sliced) ? sliced : !point.sliced;
  37359. // update userOptions.data
  37360. series.options.data[series.data.indexOf(point)] =
  37361. point.options;
  37362. point.graphic.animate(this.getTranslate());
  37363. if (point.shadowGroup) {
  37364. point.shadowGroup.animate(this.getTranslate());
  37365. }
  37366. },
  37367. /**
  37368. * @private
  37369. * @function Highcharts.seriesTypes.pie#pointClass#getTranslate
  37370. * @return {Highcharts.TranslationAttributes}
  37371. */
  37372. getTranslate: function () {
  37373. return this.sliced ? this.slicedTranslation : {
  37374. translateX: 0,
  37375. translateY: 0
  37376. };
  37377. },
  37378. /**
  37379. * @private
  37380. * @function Highcharts.seriesTypes.pie#pointClass#haloPath
  37381. * @param {number} size
  37382. * @return {Highcharts.SVGPathArray}
  37383. */
  37384. haloPath: function (size) {
  37385. var shapeArgs = this.shapeArgs;
  37386. return this.sliced || !this.visible ?
  37387. [] :
  37388. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  37389. // Substract 1px to ensure the background is not bleeding
  37390. // through between the halo and the slice (#7495).
  37391. innerR: shapeArgs.r - 1,
  37392. start: shapeArgs.start,
  37393. end: shapeArgs.end
  37394. });
  37395. },
  37396. connectorShapes: {
  37397. // only one available before v7.0.0
  37398. fixedOffset: function (labelPosition, connectorPosition, options) {
  37399. var breakAt = connectorPosition.breakAt, touchingSliceAt = connectorPosition.touchingSliceAt, linePath = options.softConnector ? [
  37400. 'C',
  37401. // 1st control point (of the curve)
  37402. labelPosition.x +
  37403. // 5 gives the connector a little horizontal bend
  37404. (labelPosition.alignment === 'left' ? -5 : 5),
  37405. labelPosition.y,
  37406. 2 * breakAt.x - touchingSliceAt.x,
  37407. 2 * breakAt.y - touchingSliceAt.y,
  37408. breakAt.x,
  37409. breakAt.y //
  37410. ] : [
  37411. 'L',
  37412. breakAt.x,
  37413. breakAt.y
  37414. ];
  37415. // assemble the path
  37416. return [
  37417. 'M',
  37418. labelPosition.x,
  37419. labelPosition.y
  37420. ]
  37421. .concat(linePath)
  37422. .concat([
  37423. 'L',
  37424. touchingSliceAt.x,
  37425. touchingSliceAt.y
  37426. ]);
  37427. },
  37428. straight: function (labelPosition, connectorPosition) {
  37429. var touchingSliceAt = connectorPosition.touchingSliceAt;
  37430. // direct line to the slice
  37431. return [
  37432. 'M',
  37433. labelPosition.x,
  37434. labelPosition.y,
  37435. 'L',
  37436. touchingSliceAt.x,
  37437. touchingSliceAt.y
  37438. ];
  37439. },
  37440. crookedLine: function (labelPosition, connectorPosition, options) {
  37441. 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
  37442. options.crookDistance, 1), crookX = alignment === 'left' ?
  37443. pieCenterX + radius + (plotWidth + plotLeft -
  37444. pieCenterX - radius) * (1 - crookDistance) :
  37445. plotLeft + (pieCenterX - radius) * crookDistance, segmentWithCrook = [
  37446. 'L',
  37447. crookX,
  37448. labelPosition.y
  37449. ];
  37450. // crookedLine formula doesn't make sense if the path overlaps
  37451. // the label - use straight line instead in that case
  37452. if (alignment === 'left' ?
  37453. (crookX > labelPosition.x || crookX < touchingSliceAt.x) :
  37454. (crookX < labelPosition.x || crookX > touchingSliceAt.x)) {
  37455. segmentWithCrook = []; // remove the crook
  37456. }
  37457. // assemble the path
  37458. return [
  37459. 'M',
  37460. labelPosition.x,
  37461. labelPosition.y
  37462. ]
  37463. .concat(segmentWithCrook)
  37464. .concat([
  37465. 'L',
  37466. touchingSliceAt.x,
  37467. touchingSliceAt.y
  37468. ]);
  37469. }
  37470. },
  37471. /**
  37472. * Extendable method for getting the path of the connector between the
  37473. * data label and the pie slice.
  37474. */
  37475. getConnectorPath: function () {
  37476. var labelPosition = this.labelPosition, options = this.series.options.dataLabels, connectorShape = options.connectorShape, predefinedShapes = this.connectorShapes;
  37477. // find out whether to use the predefined shape
  37478. if (predefinedShapes[connectorShape]) {
  37479. connectorShape = predefinedShapes[connectorShape];
  37480. }
  37481. return connectorShape.call(this, {
  37482. // pass simplified label position object for user's convenience
  37483. x: labelPosition.final.x,
  37484. y: labelPosition.final.y,
  37485. alignment: labelPosition.alignment
  37486. }, labelPosition.connectorPosition, options);
  37487. }
  37488. }
  37489. /* eslint-enable valid-jsdoc */
  37490. );
  37491. /**
  37492. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  37493. * it is inherited from [chart.type](#chart.type).
  37494. *
  37495. * @extends series,plotOptions.pie
  37496. * @excluding dataParser, dataURL, stack, xAxis, yAxis
  37497. * @product highcharts
  37498. * @apioption series.pie
  37499. */
  37500. /**
  37501. * An array of data points for the series. For the `pie` series type,
  37502. * points can be given in the following ways:
  37503. *
  37504. * 1. An array of numerical values. In this case, the numerical values will be
  37505. * interpreted as `y` options. Example:
  37506. * ```js
  37507. * data: [0, 5, 3, 5]
  37508. * ```
  37509. *
  37510. * 2. An array of objects with named values. The following snippet shows only a
  37511. * few settings, see the complete options set below. If the total number of
  37512. * data points exceeds the series'
  37513. * [turboThreshold](#series.pie.turboThreshold),
  37514. * this option is not available.
  37515. * ```js
  37516. * data: [{
  37517. * y: 1,
  37518. * name: "Point2",
  37519. * color: "#00FF00"
  37520. * }, {
  37521. * y: 7,
  37522. * name: "Point1",
  37523. * color: "#FF00FF"
  37524. * }]
  37525. * ```
  37526. *
  37527. * @sample {highcharts} highcharts/chart/reflow-true/
  37528. * Numerical values
  37529. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  37530. * Arrays of numeric x and y
  37531. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  37532. * Arrays of datetime x and y
  37533. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  37534. * Arrays of point.name and y
  37535. * @sample {highcharts} highcharts/series/data-array-of-objects/
  37536. * Config objects
  37537. *
  37538. * @type {Array<number|Array<string,(number|null)>|null|*>}
  37539. * @extends series.line.data
  37540. * @excluding marker, x
  37541. * @product highcharts
  37542. * @apioption series.pie.data
  37543. */
  37544. /**
  37545. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  37546. * @product highcharts
  37547. * @apioption series.pie.data.dataLabels
  37548. */
  37549. /**
  37550. * The sequential index of the data point in the legend.
  37551. *
  37552. * @type {number}
  37553. * @product highcharts
  37554. * @apioption series.pie.data.legendIndex
  37555. */
  37556. /**
  37557. * Whether to display a slice offset from the center.
  37558. *
  37559. * @sample {highcharts} highcharts/point/sliced/
  37560. * One sliced point
  37561. *
  37562. * @type {boolean}
  37563. * @product highcharts
  37564. * @apioption series.pie.data.sliced
  37565. */
  37566. /**
  37567. * @excluding legendItemClick
  37568. * @product highcharts
  37569. * @apioption series.pie.events
  37570. */
  37571. ''; // placeholder for transpiled doclets above
  37572. });
  37573. _registerModule(_modules, 'parts/DataLabels.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  37574. /* *
  37575. *
  37576. * (c) 2010-2019 Torstein Honsi
  37577. *
  37578. * License: www.highcharts.com/license
  37579. *
  37580. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37581. *
  37582. * */
  37583. /**
  37584. * Callback JavaScript function to format the data label as a string. Note that
  37585. * if a `format` is defined, the format takes precedence and the formatter is
  37586. * ignored.
  37587. *
  37588. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  37589. *
  37590. * @param {Highcharts.DataLabelsFormatterContextObject} this
  37591. * Data label context to format
  37592. *
  37593. * @return {number|string|null|undefined}
  37594. * Formatted data label text
  37595. */
  37596. /**
  37597. * Context for the callback function to format the data label.
  37598. *
  37599. * @interface Highcharts.DataLabelsFormatterContextObject
  37600. */ /**
  37601. * Stacked series and pies only. The point's percentage of the total.
  37602. * @name Highcharts.DataLabelsFormatterContextObject#percentage
  37603. * @type {number|undefined}
  37604. */ /**
  37605. * The point object. The point name, if defined, is available through
  37606. * `this.point.name`.
  37607. * @name Highcharts.DataLabelsFormatterContextObject#point
  37608. * @type {Highcharts.Point}
  37609. */ /**
  37610. * The series object. The series name is available through `this.series.name`.
  37611. * @name Highcharts.DataLabelsFormatterContextObject#series
  37612. * @type {Highcharts.Series}
  37613. */ /**
  37614. * Stacked series only. The total value at this point's x value.
  37615. * @name Highcharts.DataLabelsFormatterContextObject#total
  37616. * @type {number|undefined}
  37617. */ /**
  37618. * The x value.
  37619. * @name Highcharts.DataLabelsFormatterContextObject#x
  37620. * @type {number}
  37621. */ /**
  37622. * The y value.
  37623. * @name Highcharts.DataLabelsFormatterContextObject#y
  37624. * @type {number|null}
  37625. */
  37626. /**
  37627. * Options for the series data labels, appearing next to each data point.
  37628. *
  37629. * Since v6.2.0, multiple data labels can be applied to each single point by
  37630. * defining them as an array of configs.
  37631. *
  37632. * In styled mode, the data labels can be styled with the
  37633. * `.highcharts-data-label-box` and `.highcharts-data-label` class names.
  37634. *
  37635. * @see {@link https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-datalabels-enabled|Highcharts-Demo:}
  37636. * Data labels enabled
  37637. * @see {@link https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-datalabels-multiple|Highcharts-Demo:}
  37638. * Multiple data labels on a bar series
  37639. * @see {@link https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-datalabels|Highcharts-Demo:}
  37640. * Style mode example
  37641. *
  37642. * @interface Highcharts.DataLabelsOptionsObject
  37643. */
  37644. /**
  37645. * Values for handling data labels that flow outside the plot area.
  37646. *
  37647. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  37648. */
  37649. var animObject = U.animObject, arrayMax = U.arrayMax, clamp = U.clamp, defined = U.defined, extend = U.extend, isArray = U.isArray, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, splat = U.splat;
  37650. var format = H.format, merge = H.merge, noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes, stableSort = H.stableSort;
  37651. /* eslint-disable valid-jsdoc */
  37652. /**
  37653. * General distribution algorithm for distributing labels of differing size
  37654. * along a confined length in two dimensions. The algorithm takes an array of
  37655. * objects containing a size, a target and a rank. It will place the labels as
  37656. * close as possible to their targets, skipping the lowest ranked labels if
  37657. * necessary.
  37658. *
  37659. * @private
  37660. * @function Highcharts.distribute
  37661. * @param {Highcharts.DataLabelsBoxArray} boxes
  37662. * @param {number} len
  37663. * @param {number} maxDistance
  37664. * @return {void}
  37665. */
  37666. H.distribute = function (boxes, len, maxDistance) {
  37667. var i, overlapping = true, origBoxes = boxes, // Original array will be altered with added .pos
  37668. restBoxes = [], // The outranked overshoot
  37669. box, target, total = 0, reducedLen = origBoxes.reducedLen || len;
  37670. /**
  37671. * @private
  37672. */
  37673. function sortByTarget(a, b) {
  37674. return a.target - b.target;
  37675. }
  37676. // If the total size exceeds the len, remove those boxes with the lowest
  37677. // rank
  37678. i = boxes.length;
  37679. while (i--) {
  37680. total += boxes[i].size;
  37681. }
  37682. // Sort by rank, then slice away overshoot
  37683. if (total > reducedLen) {
  37684. stableSort(boxes, function (a, b) {
  37685. return (b.rank || 0) - (a.rank || 0);
  37686. });
  37687. i = 0;
  37688. total = 0;
  37689. while (total <= reducedLen) {
  37690. total += boxes[i].size;
  37691. i++;
  37692. }
  37693. restBoxes = boxes.splice(i - 1, boxes.length);
  37694. }
  37695. // Order by target
  37696. stableSort(boxes, sortByTarget);
  37697. // So far we have been mutating the original array. Now
  37698. // create a copy with target arrays
  37699. boxes = boxes.map(function (box) {
  37700. return {
  37701. size: box.size,
  37702. targets: [box.target],
  37703. align: pick(box.align, 0.5)
  37704. };
  37705. });
  37706. while (overlapping) {
  37707. // Initial positions: target centered in box
  37708. i = boxes.length;
  37709. while (i--) {
  37710. box = boxes[i];
  37711. // Composite box, average of targets
  37712. target = (Math.min.apply(0, box.targets) +
  37713. Math.max.apply(0, box.targets)) / 2;
  37714. box.pos = clamp(target - box.size * box.align, 0, len - box.size);
  37715. }
  37716. // Detect overlap and join boxes
  37717. i = boxes.length;
  37718. overlapping = false;
  37719. while (i--) {
  37720. // Overlap
  37721. if (i > 0 &&
  37722. boxes[i - 1].pos + boxes[i - 1].size >
  37723. boxes[i].pos) {
  37724. // Add this size to the previous box
  37725. boxes[i - 1].size += boxes[i].size;
  37726. boxes[i - 1].targets = boxes[i - 1]
  37727. .targets
  37728. .concat(boxes[i].targets);
  37729. boxes[i - 1].align = 0.5;
  37730. // Overlapping right, push left
  37731. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  37732. boxes[i - 1].pos = len - boxes[i - 1].size;
  37733. }
  37734. boxes.splice(i, 1); // Remove this item
  37735. overlapping = true;
  37736. }
  37737. }
  37738. }
  37739. // Add the rest (hidden boxes)
  37740. origBoxes.push.apply(origBoxes, restBoxes);
  37741. // Now the composite boxes are placed, we need to put the original boxes
  37742. // within them
  37743. i = 0;
  37744. boxes.some(function (box) {
  37745. var posInCompositeBox = 0;
  37746. if (box.targets.some(function () {
  37747. origBoxes[i].pos = box.pos + posInCompositeBox;
  37748. // If the distance between the position and the target exceeds
  37749. // maxDistance, abort the loop and decrease the length in increments
  37750. // of 10% to recursively reduce the number of visible boxes by
  37751. // rank. Once all boxes are within the maxDistance, we're good.
  37752. if (Math.abs(origBoxes[i].pos - origBoxes[i].target) >
  37753. maxDistance) {
  37754. // Reset the positions that are already set
  37755. origBoxes.slice(0, i + 1).forEach(function (box) {
  37756. delete box.pos;
  37757. });
  37758. // Try with a smaller length
  37759. origBoxes.reducedLen =
  37760. (origBoxes.reducedLen || len) - (len * 0.1);
  37761. // Recurse
  37762. if (origBoxes.reducedLen > len * 0.1) {
  37763. H.distribute(origBoxes, len, maxDistance);
  37764. }
  37765. // Exceeded maxDistance => abort
  37766. return true;
  37767. }
  37768. posInCompositeBox += origBoxes[i].size;
  37769. i++;
  37770. })) {
  37771. // Exceeded maxDistance => abort
  37772. return true;
  37773. }
  37774. });
  37775. // Add the rest (hidden) boxes and sort by target
  37776. stableSort(origBoxes, sortByTarget);
  37777. };
  37778. /**
  37779. * Draw the data labels
  37780. *
  37781. * @private
  37782. * @function Highcharts.Series#drawDataLabels
  37783. * @return {void}
  37784. * @fires Highcharts.Series#event:afterDrawDataLabels
  37785. */
  37786. Series.prototype.drawDataLabels = function () {
  37787. 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;
  37788. /**
  37789. * Handle the dataLabels.filter option.
  37790. * @private
  37791. */
  37792. function applyFilter(point, options) {
  37793. var filter = options.filter, op, prop, val;
  37794. if (filter) {
  37795. op = filter.operator;
  37796. prop = point[filter.property];
  37797. val = filter.value;
  37798. if ((op === '>' && prop > val) ||
  37799. (op === '<' && prop < val) ||
  37800. (op === '>=' && prop >= val) ||
  37801. (op === '<=' && prop <= val) ||
  37802. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  37803. (op === '===' && prop === val)) {
  37804. return true;
  37805. }
  37806. return false;
  37807. }
  37808. return true;
  37809. }
  37810. /**
  37811. * Merge two objects that can be arrays. If one of them is an array, the
  37812. * other is merged into each element. If both are arrays, each element is
  37813. * merged by index. If neither are arrays, we use normal merge.
  37814. * @private
  37815. */
  37816. function mergeArrays(one, two) {
  37817. var res = [], i;
  37818. if (isArray(one) && !isArray(two)) {
  37819. res = one.map(function (el) {
  37820. return merge(el, two);
  37821. });
  37822. }
  37823. else if (isArray(two) && !isArray(one)) {
  37824. res = two.map(function (el) {
  37825. return merge(one, el);
  37826. });
  37827. }
  37828. else if (!isArray(one) && !isArray(two)) {
  37829. res = merge(one, two);
  37830. }
  37831. else {
  37832. i = Math.max(one.length, two.length);
  37833. while (i--) {
  37834. res[i] = merge(one[i], two[i]);
  37835. }
  37836. }
  37837. return res;
  37838. }
  37839. // Merge in plotOptions.dataLabels for series
  37840. seriesDlOptions = mergeArrays(mergeArrays(chart.options.plotOptions &&
  37841. chart.options.plotOptions.series &&
  37842. chart.options.plotOptions.series.dataLabels, chart.options.plotOptions &&
  37843. chart.options.plotOptions[series.type] &&
  37844. chart.options.plotOptions[series.type].dataLabels), seriesDlOptions);
  37845. H.fireEvent(this, 'drawDataLabels');
  37846. if (isArray(seriesDlOptions) ||
  37847. seriesDlOptions.enabled ||
  37848. series._hasPointLabels) {
  37849. // Create a separate group for the data labels to avoid rotation
  37850. dataLabelsGroup = series.plotGroup('dataLabelsGroup', 'data-labels', defer && !hasRendered ? 'hidden' : 'inherit', // #5133, #10220
  37851. seriesDlOptions.zIndex || 6);
  37852. if (defer) {
  37853. dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
  37854. if (!hasRendered) {
  37855. setTimeout(function () {
  37856. var group = series.dataLabelsGroup;
  37857. if (group) {
  37858. if (series.visible) { // #2597, #3023, #3024
  37859. dataLabelsGroup.show(true);
  37860. }
  37861. group[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: fadeInDuration });
  37862. }
  37863. }, seriesAnimDuration - fadeInDuration);
  37864. }
  37865. }
  37866. // Make the labels for each point
  37867. points.forEach(function (point) {
  37868. // Merge in series options for the point.
  37869. // @note dataLabelAttribs (like pointAttribs) would eradicate
  37870. // the need for dlOptions, and simplify the section below.
  37871. pointOptions = splat(mergeArrays(seriesDlOptions, point.dlOptions || // dlOptions is used in treemaps
  37872. (point.options && point.options.dataLabels)));
  37873. // Handle each individual data label for this point
  37874. pointOptions.forEach(function (labelOptions, i) {
  37875. // Options for one datalabel
  37876. var labelEnabled = (labelOptions.enabled &&
  37877. // #2282, #4641, #7112, #10049
  37878. (!point.isNull || point.dataLabelOnNull) &&
  37879. applyFilter(point, labelOptions)), labelConfig, formatString, labelText, style, rotation, attr, dataLabel = point.dataLabels ? point.dataLabels[i] :
  37880. point.dataLabel, connector = point.connectors ? point.connectors[i] :
  37881. point.connector, labelDistance = pick(labelOptions.distance, point.labelDistance), isNew = !dataLabel;
  37882. if (labelEnabled) {
  37883. // Create individual options structure that can be extended
  37884. // without affecting others
  37885. labelConfig = point.getLabelConfig();
  37886. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  37887. labelText = defined(formatString) ?
  37888. format(formatString, labelConfig, chart) :
  37889. (labelOptions[point.formatPrefix + 'Formatter'] ||
  37890. labelOptions.formatter).call(labelConfig, labelOptions);
  37891. style = labelOptions.style;
  37892. rotation = labelOptions.rotation;
  37893. if (!chart.styledMode) {
  37894. // Determine the color
  37895. style.color = pick(labelOptions.color, style.color, series.color, '#000000');
  37896. // Get automated contrast color
  37897. if (style.color === 'contrast') {
  37898. point.contrastColor = renderer.getContrast((point.color || series.color));
  37899. style.color = (!defined(labelDistance) &&
  37900. labelOptions.inside) ||
  37901. labelDistance < 0 ||
  37902. !!seriesOptions.stacking ?
  37903. point.contrastColor :
  37904. '#000000';
  37905. }
  37906. else {
  37907. delete point.contrastColor;
  37908. }
  37909. if (seriesOptions.cursor) {
  37910. style.cursor = seriesOptions.cursor;
  37911. }
  37912. }
  37913. attr = {
  37914. r: labelOptions.borderRadius || 0,
  37915. rotation: rotation,
  37916. padding: labelOptions.padding,
  37917. zIndex: 1
  37918. };
  37919. if (!chart.styledMode) {
  37920. attr.fill = labelOptions.backgroundColor;
  37921. attr.stroke = labelOptions.borderColor;
  37922. attr['stroke-width'] = labelOptions.borderWidth;
  37923. }
  37924. // Remove unused attributes (#947)
  37925. objectEach(attr, function (val, name) {
  37926. if (typeof val === 'undefined') {
  37927. delete attr[name];
  37928. }
  37929. });
  37930. }
  37931. // If the point is outside the plot area, destroy it. #678, #820
  37932. if (dataLabel && (!labelEnabled || !defined(labelText))) {
  37933. point.dataLabel =
  37934. point.dataLabel && point.dataLabel.destroy();
  37935. if (point.dataLabels) {
  37936. // Remove point.dataLabels if this was the last one
  37937. if (point.dataLabels.length === 1) {
  37938. delete point.dataLabels;
  37939. }
  37940. else {
  37941. delete point.dataLabels[i];
  37942. }
  37943. }
  37944. if (!i) {
  37945. delete point.dataLabel;
  37946. }
  37947. if (connector) {
  37948. point.connector = point.connector.destroy();
  37949. if (point.connectors) {
  37950. // Remove point.connectors if this was the last one
  37951. if (point.connectors.length === 1) {
  37952. delete point.connectors;
  37953. }
  37954. else {
  37955. delete point.connectors[i];
  37956. }
  37957. }
  37958. }
  37959. // Individual labels are disabled if the are explicitly disabled
  37960. // in the point options, or if they fall outside the plot area.
  37961. }
  37962. else if (labelEnabled && defined(labelText)) {
  37963. if (!dataLabel) {
  37964. // Create new label element
  37965. point.dataLabels = point.dataLabels || [];
  37966. dataLabel = point.dataLabels[i] = rotation ?
  37967. // Labels don't rotate, use text element
  37968. renderer.text(labelText, 0, -9999)
  37969. .addClass('highcharts-data-label') :
  37970. // We can use label
  37971. renderer.label(labelText, 0, -9999, labelOptions.shape, null, null, labelOptions.useHTML, null, 'data-label');
  37972. // Store for backwards compatibility
  37973. if (!i) {
  37974. point.dataLabel = dataLabel;
  37975. }
  37976. dataLabel.addClass(' highcharts-data-label-color-' + point.colorIndex +
  37977. ' ' + (labelOptions.className || '') +
  37978. ( // #3398
  37979. labelOptions.useHTML ?
  37980. ' highcharts-tracker' :
  37981. ''));
  37982. }
  37983. else {
  37984. // Use old element and just update text
  37985. attr.text = labelText;
  37986. }
  37987. // Store data label options for later access
  37988. dataLabel.options = labelOptions;
  37989. dataLabel.attr(attr);
  37990. if (!chart.styledMode) {
  37991. // Styles must be applied before add in order to read
  37992. // text bounding box
  37993. dataLabel.css(style).shadow(labelOptions.shadow);
  37994. }
  37995. if (!dataLabel.added) {
  37996. dataLabel.add(dataLabelsGroup);
  37997. }
  37998. if (labelOptions.textPath && !labelOptions.useHTML) {
  37999. dataLabel.setTextPath((point.getDataLabelPath &&
  38000. point.getDataLabelPath(dataLabel)) || point.graphic, labelOptions.textPath);
  38001. if (point.dataLabelPath &&
  38002. !labelOptions.textPath.enabled) {
  38003. // clean the DOM
  38004. point.dataLabelPath = point.dataLabelPath.destroy();
  38005. }
  38006. }
  38007. // Now the data label is created and placed at 0,0, so we
  38008. // need to align it
  38009. series.alignDataLabel(point, dataLabel, labelOptions, null, isNew);
  38010. }
  38011. });
  38012. });
  38013. }
  38014. H.fireEvent(this, 'afterDrawDataLabels');
  38015. };
  38016. /**
  38017. * Align each individual data label.
  38018. *
  38019. * @private
  38020. * @function Highcharts.Series#alignDataLabel
  38021. * @param {Highcharts.Point} point
  38022. * @param {Highcharts.SVGElement} dataLabel
  38023. * @param {Highcharts.DataLabelsOptionsObject} options
  38024. * @param {Highcharts.BBoxObject} alignTo
  38025. * @param {boolean} [isNew]
  38026. * @return {void}
  38027. */
  38028. Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  38029. 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
  38030. isInsidePlot = chart.isInsidePlot(plotX, Math.round(plotY), inverted),
  38031. // Math.round for rounding errors (#2683), alignTo to allow column
  38032. // labels (#2700)
  38033. alignAttr, // the final position;
  38034. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  38035. (point.series.forceDL ||
  38036. (enabledDataSorting && !justify) ||
  38037. isInsidePlot ||
  38038. (alignTo && chart.isInsidePlot(plotX, inverted ?
  38039. alignTo.x + 1 :
  38040. alignTo.y + alignTo.height - 1, inverted))), setStartPos = function (alignOptions) {
  38041. if (enabledDataSorting && series.xAxis && !justify) {
  38042. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  38043. }
  38044. };
  38045. if (visible) {
  38046. baseline = chart.renderer.fontMetrics(chart.styledMode ? void 0 : options.style.fontSize, dataLabel).b;
  38047. // The alignment box is a singular point
  38048. alignTo = extend({
  38049. x: inverted ? this.yAxis.len - plotY : plotX,
  38050. y: Math.round(inverted ? this.xAxis.len - plotX : plotY),
  38051. width: 0,
  38052. height: 0
  38053. }, alignTo);
  38054. // Add the text size for alignment calculation
  38055. extend(options, {
  38056. width: bBox.width,
  38057. height: bBox.height
  38058. });
  38059. // Allow a hook for changing alignment in the last moment, then do the
  38060. // alignment
  38061. if (rotation) {
  38062. justify = false; // Not supported for rotated text
  38063. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  38064. alignAttr = {
  38065. x: (alignTo.x +
  38066. options.x +
  38067. alignTo.width / 2 +
  38068. rotCorr.x),
  38069. y: (alignTo.y +
  38070. options.y +
  38071. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  38072. alignTo.height)
  38073. };
  38074. setStartPos(alignAttr); // data sorting
  38075. dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
  38076. .attr({
  38077. align: align
  38078. });
  38079. // Compensate for the rotated label sticking out on the sides
  38080. normRotation = (rotation + 720) % 360;
  38081. negRotation = normRotation > 180 && normRotation < 360;
  38082. if (align === 'left') {
  38083. alignAttr.y -= negRotation ? bBox.height : 0;
  38084. }
  38085. else if (align === 'center') {
  38086. alignAttr.x -= bBox.width / 2;
  38087. alignAttr.y -= bBox.height / 2;
  38088. }
  38089. else if (align === 'right') {
  38090. alignAttr.x -= bBox.width;
  38091. alignAttr.y -= negRotation ? 0 : bBox.height;
  38092. }
  38093. dataLabel.placed = true;
  38094. dataLabel.alignAttr = alignAttr;
  38095. }
  38096. else {
  38097. setStartPos(alignTo); // data sorting
  38098. dataLabel.align(options, null, alignTo);
  38099. alignAttr = dataLabel.alignAttr;
  38100. }
  38101. // Handle justify or crop
  38102. if (justify && alignTo.height >= 0) { // #8830
  38103. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  38104. // Now check that the data label is within the plot area
  38105. }
  38106. else if (pick(options.crop, true)) {
  38107. visible =
  38108. chart.isInsidePlot(alignAttr.x, alignAttr.y) &&
  38109. chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height);
  38110. }
  38111. // When we're using a shape, make it possible with a connector or an
  38112. // arrow pointing to thie point
  38113. if (options.shape && !rotation) {
  38114. dataLabel[isNew ? 'attr' : 'animate']({
  38115. anchorX: inverted ?
  38116. chart.plotWidth - point.plotY :
  38117. point.plotX,
  38118. anchorY: inverted ?
  38119. chart.plotHeight - point.plotX :
  38120. point.plotY
  38121. });
  38122. }
  38123. }
  38124. // To use alignAttr property in hideOverlappingLabels
  38125. if (isNew && enabledDataSorting) {
  38126. dataLabel.placed = false;
  38127. }
  38128. // Show or hide based on the final aligned position
  38129. if (!visible && (!enabledDataSorting || justify)) {
  38130. dataLabel.hide(true);
  38131. dataLabel.placed = false; // don't animate back in
  38132. }
  38133. };
  38134. /**
  38135. * Set starting position for data label sorting animation.
  38136. *
  38137. * @private
  38138. * @function Highcharts.Series#setDataLabelStartPos
  38139. * @param {Highcharts.SVGElement} dataLabel
  38140. * @param {Highcharts.ColumnPoint} point
  38141. * @param {boolean | undefined} [isNew]
  38142. * @param {boolean} [isInside]
  38143. * @param {Highcharts.AlignObject} [alignOptions]
  38144. *
  38145. * @return {void}
  38146. */
  38147. Series.prototype.setDataLabelStartPos = function (point, dataLabel, isNew, isInside, alignOptions) {
  38148. 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;
  38149. startXPos = inverted ?
  38150. alignOptions.x :
  38151. (reversed ?
  38152. -labelCenter - halfWidth :
  38153. xAxis.width - labelCenter + halfWidth);
  38154. startYPos = inverted ?
  38155. (reversed ?
  38156. this.yAxis.height - labelCenter + halfWidth :
  38157. -labelCenter - halfWidth) : alignOptions.y;
  38158. dataLabel.startXPos = startXPos;
  38159. dataLabel.startYPos = startYPos;
  38160. // We need to handle visibility in case of sorting point outside plot area
  38161. if (!isInside) {
  38162. dataLabel
  38163. .attr({ opacity: 1 })
  38164. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  38165. }
  38166. else if (dataLabel.visibility === 'hidden') {
  38167. dataLabel.show();
  38168. dataLabel
  38169. .attr({ opacity: 0 })
  38170. .animate({ opacity: 1 });
  38171. }
  38172. // Save start position on first render, but do not change position
  38173. if (!chart.hasRendered) {
  38174. return;
  38175. }
  38176. // Set start position
  38177. if (isNew) {
  38178. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  38179. }
  38180. dataLabel.placed = true;
  38181. };
  38182. /**
  38183. * If data labels fall partly outside the plot area, align them back in, in a
  38184. * way that doesn't hide the point.
  38185. *
  38186. * @private
  38187. * @function Highcharts.Series#justifyDataLabel
  38188. * @param {Highcharts.SVGElement} dataLabel
  38189. * @param {Highcharts.DataLabelsOptionsObject} options
  38190. * @param {Highcharts.SVGAttributes} alignAttr
  38191. * @param {Highcharts.BBoxObject} bBox
  38192. * @param {Highcharts.BBoxObject} [alignTo]
  38193. * @param {boolean} [isNew]
  38194. * @return {boolean|undefined}
  38195. */
  38196. Series.prototype.justifyDataLabel = function (dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  38197. var chart = this.chart, align = options.align, verticalAlign = options.verticalAlign, off, justified, padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  38198. // Off left
  38199. off = alignAttr.x + padding;
  38200. if (off < 0) {
  38201. if (align === 'right') {
  38202. options.align = 'left';
  38203. options.inside = true;
  38204. }
  38205. else {
  38206. options.x = -off;
  38207. }
  38208. justified = true;
  38209. }
  38210. // Off right
  38211. off = alignAttr.x + bBox.width - padding;
  38212. if (off > chart.plotWidth) {
  38213. if (align === 'left') {
  38214. options.align = 'right';
  38215. options.inside = true;
  38216. }
  38217. else {
  38218. options.x = chart.plotWidth - off;
  38219. }
  38220. justified = true;
  38221. }
  38222. // Off top
  38223. off = alignAttr.y + padding;
  38224. if (off < 0) {
  38225. if (verticalAlign === 'bottom') {
  38226. options.verticalAlign = 'top';
  38227. options.inside = true;
  38228. }
  38229. else {
  38230. options.y = -off;
  38231. }
  38232. justified = true;
  38233. }
  38234. // Off bottom
  38235. off = alignAttr.y + bBox.height - padding;
  38236. if (off > chart.plotHeight) {
  38237. if (verticalAlign === 'top') {
  38238. options.verticalAlign = 'bottom';
  38239. options.inside = true;
  38240. }
  38241. else {
  38242. options.y = chart.plotHeight - off;
  38243. }
  38244. justified = true;
  38245. }
  38246. if (justified) {
  38247. dataLabel.placed = !isNew;
  38248. dataLabel.align(options, null, alignTo);
  38249. }
  38250. return justified;
  38251. };
  38252. if (seriesTypes.pie) {
  38253. seriesTypes.pie.prototype.dataLabelPositioners = {
  38254. // Based on the value computed in Highcharts' distribute algorithm.
  38255. radialDistributionY: function (point) {
  38256. return point.top + point.distributeBox.pos;
  38257. },
  38258. // get the x - use the natural x position for labels near the
  38259. // top and bottom, to prevent the top and botton slice
  38260. // connectors from touching each other on either side
  38261. // Based on the value computed in Highcharts' distribute algorithm.
  38262. radialDistributionX: function (series, point, y, naturalY) {
  38263. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  38264. naturalY :
  38265. y, point.half, point);
  38266. },
  38267. // dataLabels.distance determines the x position of the label
  38268. justify: function (point, radius, seriesCenter) {
  38269. return seriesCenter[0] + (point.half ? -1 : 1) *
  38270. (radius + point.labelDistance);
  38271. },
  38272. // Left edges of the left-half labels touch the left edge of the plot
  38273. // area. Right edges of the right-half labels touch the right edge of
  38274. // the plot area.
  38275. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  38276. var dataLabelWidth = dataLabel.getBBox().width;
  38277. return half ? dataLabelWidth + plotLeft :
  38278. plotWidth - dataLabelWidth - plotLeft;
  38279. },
  38280. // Connectors of each side end in the same x position. Labels are
  38281. // aligned to them. Left edge of the widest left-half label touches the
  38282. // left edge of the plot area. Right edge of the widest right-half label
  38283. // touches the right edge of the plot area.
  38284. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  38285. var maxDataLabelWidth = 0, dataLabelWidth;
  38286. // find widest data label
  38287. points.forEach(function (point) {
  38288. dataLabelWidth = point.dataLabel.getBBox().width;
  38289. if (dataLabelWidth > maxDataLabelWidth) {
  38290. maxDataLabelWidth = dataLabelWidth;
  38291. }
  38292. });
  38293. return half ? maxDataLabelWidth + plotLeft :
  38294. plotWidth - maxDataLabelWidth - plotLeft;
  38295. }
  38296. };
  38297. /**
  38298. * Override the base drawDataLabels method by pie specific functionality
  38299. *
  38300. * @private
  38301. * @function Highcharts.seriesTypes.pie#drawDataLabels
  38302. * @return {void}
  38303. */
  38304. seriesTypes.pie.prototype.drawDataLabels = function () {
  38305. 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,
  38306. // labelPos,
  38307. labelPosition, labelHeight,
  38308. // divide the points into right and left halves for anti collision
  38309. halves = [
  38310. [],
  38311. [] // left
  38312. ], x, y, visibility, j, overflow = [0, 0, 0, 0], // top, right, bottom, left
  38313. dataLabelPositioners = series.dataLabelPositioners, pointDataLabelsOptions;
  38314. // get out if not enabled
  38315. if (!series.visible ||
  38316. (!options.enabled &&
  38317. !series._hasPointLabels)) {
  38318. return;
  38319. }
  38320. // Reset all labels that have been shortened
  38321. data.forEach(function (point) {
  38322. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  38323. point.dataLabel
  38324. .attr({
  38325. width: 'auto'
  38326. }).css({
  38327. width: 'auto',
  38328. textOverflow: 'clip'
  38329. });
  38330. point.dataLabel.shortened = false;
  38331. }
  38332. });
  38333. // run parent method
  38334. Series.prototype.drawDataLabels.apply(series);
  38335. data.forEach(function (point) {
  38336. if (point.dataLabel) {
  38337. if (point.visible) { // #407, #2510
  38338. // Arrange points for detection collision
  38339. halves[point.half].push(point);
  38340. // Reset positions (#4905)
  38341. point.dataLabel._pos = null;
  38342. // Avoid long labels squeezing the pie size too far down
  38343. if (!defined(options.style.width) &&
  38344. !defined(point.options.dataLabels &&
  38345. point.options.dataLabels.style &&
  38346. point.options.dataLabels.style.width)) {
  38347. if (point.dataLabel.getBBox().width > maxWidth) {
  38348. point.dataLabel.css({
  38349. // Use a fraction of the maxWidth to avoid
  38350. // wrapping close to the end of the string.
  38351. width: maxWidth * 0.7
  38352. });
  38353. point.dataLabel.shortened = true;
  38354. }
  38355. }
  38356. }
  38357. else {
  38358. point.dataLabel = point.dataLabel.destroy();
  38359. // Workaround to make pies destroy multiple datalabels
  38360. // correctly. This logic needs rewriting to support multiple
  38361. // datalabels fully.
  38362. if (point.dataLabels && point.dataLabels.length === 1) {
  38363. delete point.dataLabels;
  38364. }
  38365. }
  38366. }
  38367. });
  38368. /* Loop over the points in each half, starting from the top and bottom
  38369. * of the pie to detect overlapping labels.
  38370. */
  38371. halves.forEach(function (points, i) {
  38372. var top, bottom, length = points.length, positions = [], naturalY, sideOverflow, size, distributionLength;
  38373. if (!length) {
  38374. return;
  38375. }
  38376. // Sort by angle
  38377. series.sortByAngle(points, i - 0.5);
  38378. // Only do anti-collision when we have dataLabels outside the pie
  38379. // and have connectors. (#856)
  38380. if (series.maxLabelDistance > 0) {
  38381. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  38382. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  38383. points.forEach(function (point) {
  38384. // check if specific points' label is outside the pie
  38385. if (point.labelDistance > 0 && point.dataLabel) {
  38386. // point.top depends on point.labelDistance value
  38387. // Used for calculation of y value in getX method
  38388. point.top = Math.max(0, centerY - radius - point.labelDistance);
  38389. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  38390. size = point.dataLabel.getBBox().height || 21;
  38391. // point.positionsIndex is needed for getting index of
  38392. // parameter related to specific point inside positions
  38393. // array - not every point is in positions array.
  38394. point.distributeBox = {
  38395. target: point.labelPosition.natural.y -
  38396. point.top + size / 2,
  38397. size: size,
  38398. rank: point.y
  38399. };
  38400. positions.push(point.distributeBox);
  38401. }
  38402. });
  38403. distributionLength = bottom + size - top;
  38404. H.distribute(positions, distributionLength, distributionLength / 5);
  38405. }
  38406. // Now the used slots are sorted, fill them up sequentially
  38407. for (j = 0; j < length; j++) {
  38408. point = points[j];
  38409. // labelPos = point.labelPos;
  38410. labelPosition = point.labelPosition;
  38411. dataLabel = point.dataLabel;
  38412. visibility = point.visible === false ? 'hidden' : 'inherit';
  38413. naturalY = labelPosition.natural.y;
  38414. y = naturalY;
  38415. if (positions && defined(point.distributeBox)) {
  38416. if (typeof point.distributeBox.pos === 'undefined') {
  38417. visibility = 'hidden';
  38418. }
  38419. else {
  38420. labelHeight = point.distributeBox.size;
  38421. // Find label's y position
  38422. y = dataLabelPositioners
  38423. .radialDistributionY(point);
  38424. }
  38425. }
  38426. // It is needed to delete point.positionIndex for
  38427. // dynamically added points etc.
  38428. delete point.positionIndex; // @todo unused
  38429. // Find label's x position
  38430. // justify is undocumented in the API - preserve support for it
  38431. if (options.justify) {
  38432. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  38433. }
  38434. else {
  38435. switch (options.alignTo) {
  38436. case 'connectors':
  38437. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  38438. break;
  38439. case 'plotEdges':
  38440. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  38441. break;
  38442. default:
  38443. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  38444. }
  38445. }
  38446. // Record the placement and visibility
  38447. dataLabel._attr = {
  38448. visibility: visibility,
  38449. align: labelPosition.alignment
  38450. };
  38451. dataLabel._pos = {
  38452. x: (x +
  38453. options.x +
  38454. ({
  38455. left: connectorPadding,
  38456. right: -connectorPadding
  38457. }[labelPosition.alignment] || 0)),
  38458. // 10 is for the baseline (label vs text)
  38459. y: y + options.y - 10
  38460. };
  38461. // labelPos.x = x;
  38462. // labelPos.y = y;
  38463. labelPosition.final.x = x;
  38464. labelPosition.final.y = y;
  38465. // Detect overflowing data labels
  38466. if (pick(options.crop, true)) {
  38467. dataLabelWidth = dataLabel.getBBox().width;
  38468. sideOverflow = null;
  38469. // Overflow left
  38470. if (x - dataLabelWidth < connectorPadding &&
  38471. i === 1 // left half
  38472. ) {
  38473. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  38474. overflow[3] = Math.max(sideOverflow, overflow[3]);
  38475. // Overflow right
  38476. }
  38477. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  38478. i === 0 // right half
  38479. ) {
  38480. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  38481. overflow[1] = Math.max(sideOverflow, overflow[1]);
  38482. }
  38483. // Overflow top
  38484. if (y - labelHeight / 2 < 0) {
  38485. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  38486. // Overflow left
  38487. }
  38488. else if (y + labelHeight / 2 > plotHeight) {
  38489. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  38490. }
  38491. dataLabel.sideOverflow = sideOverflow;
  38492. }
  38493. } // for each point
  38494. }); // for each half
  38495. // Do not apply the final placement and draw the connectors until we
  38496. // have verified that labels are not spilling over.
  38497. if (arrayMax(overflow) === 0 ||
  38498. this.verifyDataLabelOverflow(overflow)) {
  38499. // Place the labels in the final position
  38500. this.placeDataLabels();
  38501. this.points.forEach(function (point) {
  38502. // #8864: every connector can have individual options
  38503. pointDataLabelsOptions =
  38504. merge(options, point.options.dataLabels);
  38505. connectorWidth =
  38506. pick(pointDataLabelsOptions.connectorWidth, 1);
  38507. // Draw the connector
  38508. if (connectorWidth) {
  38509. var isNew;
  38510. connector = point.connector;
  38511. dataLabel = point.dataLabel;
  38512. if (dataLabel &&
  38513. dataLabel._pos &&
  38514. point.visible &&
  38515. point.labelDistance > 0) {
  38516. visibility = dataLabel._attr.visibility;
  38517. isNew = !connector;
  38518. if (isNew) {
  38519. point.connector = connector = chart.renderer
  38520. .path()
  38521. .addClass('highcharts-data-label-connector ' +
  38522. ' highcharts-color-' + point.colorIndex +
  38523. (point.className ?
  38524. ' ' + point.className :
  38525. ''))
  38526. .add(series.dataLabelsGroup);
  38527. if (!chart.styledMode) {
  38528. connector.attr({
  38529. 'stroke-width': connectorWidth,
  38530. 'stroke': (pointDataLabelsOptions.connectorColor ||
  38531. point.color ||
  38532. '#666666')
  38533. });
  38534. }
  38535. }
  38536. connector[isNew ? 'attr' : 'animate']({
  38537. d: point.getConnectorPath()
  38538. });
  38539. connector.attr('visibility', visibility);
  38540. }
  38541. else if (connector) {
  38542. point.connector = connector.destroy();
  38543. }
  38544. }
  38545. });
  38546. }
  38547. };
  38548. /**
  38549. * Extendable method for getting the path of the connector between the data
  38550. * label and the pie slice.
  38551. *
  38552. * @private
  38553. * @function Highcharts.seriesTypes.pie#connectorPath
  38554. *
  38555. * @param {*} labelPos
  38556. *
  38557. * @return {Highcharts.SVGPathArray}
  38558. */
  38559. // TODO: depracated - remove it
  38560. /*
  38561. seriesTypes.pie.prototype.connectorPath = function (labelPos) {
  38562. var x = labelPos.x,
  38563. y = labelPos.y;
  38564. return pick(this.options.dataLabels.softConnector, true) ? [
  38565. 'M',
  38566. // end of the string at the label
  38567. x + (labelPos[6] === 'left' ? 5 : -5), y,
  38568. 'C',
  38569. x, y, // first break, next to the label
  38570. 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
  38571. labelPos[2], labelPos[3], // second break
  38572. 'L',
  38573. labelPos[4], labelPos[5] // base
  38574. ] : [
  38575. 'M',
  38576. // end of the string at the label
  38577. x + (labelPos[6] === 'left' ? 5 : -5), y,
  38578. 'L',
  38579. labelPos[2], labelPos[3], // second break
  38580. 'L',
  38581. labelPos[4], labelPos[5] // base
  38582. ];
  38583. };
  38584. */
  38585. /**
  38586. * Perform the final placement of the data labels after we have verified
  38587. * that they fall within the plot area.
  38588. *
  38589. * @private
  38590. * @function Highcharts.seriesTypes.pie#placeDataLabels
  38591. * @return {void}
  38592. */
  38593. seriesTypes.pie.prototype.placeDataLabels = function () {
  38594. this.points.forEach(function (point) {
  38595. var dataLabel = point.dataLabel, _pos;
  38596. if (dataLabel && point.visible) {
  38597. _pos = dataLabel._pos;
  38598. if (_pos) {
  38599. // Shorten data labels with ellipsis if they still overflow
  38600. // after the pie has reached minSize (#223).
  38601. if (dataLabel.sideOverflow) {
  38602. dataLabel._attr.width =
  38603. Math.max(dataLabel.getBBox().width -
  38604. dataLabel.sideOverflow, 0);
  38605. dataLabel.css({
  38606. width: dataLabel._attr.width + 'px',
  38607. textOverflow: ((this.options.dataLabels.style || {})
  38608. .textOverflow ||
  38609. 'ellipsis')
  38610. });
  38611. dataLabel.shortened = true;
  38612. }
  38613. dataLabel.attr(dataLabel._attr);
  38614. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  38615. dataLabel.moved = true;
  38616. }
  38617. else if (dataLabel) {
  38618. dataLabel.attr({ y: -9999 });
  38619. }
  38620. }
  38621. // Clear for update
  38622. delete point.distributeBox;
  38623. }, this);
  38624. };
  38625. seriesTypes.pie.prototype.alignDataLabel = noop;
  38626. /**
  38627. * Verify whether the data labels are allowed to draw, or we should run more
  38628. * translation and data label positioning to keep them inside the plot area.
  38629. * Returns true when data labels are ready to draw.
  38630. *
  38631. * @private
  38632. * @function Highcharts.seriesTypes.pie#verifyDataLabelOverflow
  38633. * @param {Array<number>} overflow
  38634. * @return {boolean}
  38635. */
  38636. seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
  38637. var center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
  38638. // If a size is set, return true and don't try to shrink the pie
  38639. // to fit the labels.
  38640. ret = options.size !== null;
  38641. if (!ret) {
  38642. // Handle horizontal size and center
  38643. if (centerOption[0] !== null) { // Fixed center
  38644. newSize = Math.max(center[2] -
  38645. Math.max(overflow[1], overflow[3]), minSize);
  38646. }
  38647. else { // Auto center
  38648. newSize = Math.max(
  38649. // horizontal overflow
  38650. center[2] - overflow[1] - overflow[3], minSize);
  38651. // horizontal center
  38652. center[0] += (overflow[3] - overflow[1]) / 2;
  38653. }
  38654. // Handle vertical size and center
  38655. if (centerOption[1] !== null) { // Fixed center
  38656. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  38657. }
  38658. else { // Auto center
  38659. newSize = clamp(newSize, minSize,
  38660. // vertical overflow
  38661. center[2] - overflow[0] - overflow[2]);
  38662. // vertical center
  38663. center[1] += (overflow[0] - overflow[2]) / 2;
  38664. }
  38665. // If the size must be decreased, we need to run translate and
  38666. // drawDataLabels again
  38667. if (newSize < center[2]) {
  38668. center[2] = newSize;
  38669. center[3] = Math.min(// #3632
  38670. relativeLength(options.innerSize || 0, newSize), newSize);
  38671. this.translate(center);
  38672. if (this.drawDataLabels) {
  38673. this.drawDataLabels();
  38674. }
  38675. // Else, return true to indicate that the pie and its labels is
  38676. // within the plot area
  38677. }
  38678. else {
  38679. ret = true;
  38680. }
  38681. }
  38682. return ret;
  38683. };
  38684. }
  38685. if (seriesTypes.column) {
  38686. /**
  38687. * Override the basic data label alignment by adjusting for the position of
  38688. * the column.
  38689. *
  38690. * @private
  38691. * @function Highcharts.seriesTypes.column#alignDataLabel
  38692. * @param {Highcharts.Point} point
  38693. * @param {Highcharts.SVGElement} dataLabel
  38694. * @param {Highcharts.DataLabelsOptionsObject} options
  38695. * @param {Highcharts.BBoxObject} alignTo
  38696. * @param {boolean} [isNew]
  38697. * @return {void}
  38698. */
  38699. seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  38700. var inverted = this.chart.inverted, series = point.series,
  38701. // data label box for alignment
  38702. dlBox = point.dlBox || point.shapeArgs, below = pick(point.below, // range series
  38703. point.plotY >
  38704. pick(this.translatedThreshold, series.yAxis.len)),
  38705. // draw it inside the box?
  38706. inside = pick(options.inside, !!this.options.stacking), overshoot;
  38707. // Align to the column itself, or the top of it
  38708. if (dlBox) { // Area range uses this method but not alignTo
  38709. alignTo = merge(dlBox);
  38710. if (alignTo.y < 0) {
  38711. alignTo.height += alignTo.y;
  38712. alignTo.y = 0;
  38713. }
  38714. overshoot = alignTo.y + alignTo.height - series.yAxis.len;
  38715. if (overshoot > 0) {
  38716. alignTo.height -= overshoot;
  38717. }
  38718. if (inverted) {
  38719. alignTo = {
  38720. x: series.yAxis.len - alignTo.y - alignTo.height,
  38721. y: series.xAxis.len - alignTo.x - alignTo.width,
  38722. width: alignTo.height,
  38723. height: alignTo.width
  38724. };
  38725. }
  38726. // Compute the alignment box
  38727. if (!inside) {
  38728. if (inverted) {
  38729. alignTo.x += below ? 0 : alignTo.width;
  38730. alignTo.width = 0;
  38731. }
  38732. else {
  38733. alignTo.y += below ? alignTo.height : 0;
  38734. alignTo.height = 0;
  38735. }
  38736. }
  38737. }
  38738. // When alignment is undefined (typically columns and bars), display the
  38739. // individual point below or above the point depending on the threshold
  38740. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  38741. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  38742. // Call the parent method
  38743. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  38744. // Hide dataLabel when column is outside plotArea (#12370).
  38745. if (alignTo &&
  38746. ((alignTo.height <= 0 && alignTo.y === this.chart.plotHeight) ||
  38747. (alignTo.width <= 0 && alignTo.x === 0))) {
  38748. dataLabel.hide(true);
  38749. dataLabel.placed = false; // don't animate back in
  38750. }
  38751. // If label was justified and we have contrast, set it:
  38752. if (options.inside && point.contrastColor) {
  38753. dataLabel.css({
  38754. color: point.contrastColor
  38755. });
  38756. }
  38757. };
  38758. }
  38759. });
  38760. _registerModule(_modules, 'modules/overlapping-datalabels.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  38761. /* *
  38762. *
  38763. * Highcharts module to hide overlapping data labels.
  38764. * This module is included in Highcharts.
  38765. *
  38766. * (c) 2009-2019 Torstein Honsi
  38767. *
  38768. * License: www.highcharts.com/license
  38769. *
  38770. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38771. *
  38772. * */
  38773. var isArray = U.isArray, objectEach = U.objectEach, pick = U.pick;
  38774. var Chart = H.Chart, addEvent = H.addEvent, fireEvent = H.fireEvent;
  38775. /* eslint-disable no-invalid-this */
  38776. // Collect potensial overlapping data labels. Stack labels probably don't need
  38777. // to be considered because they are usually accompanied by data labels that lie
  38778. // inside the columns.
  38779. addEvent(Chart, 'render', function collectAndHide() {
  38780. var labels = [];
  38781. // Consider external label collectors
  38782. (this.labelCollectors || []).forEach(function (collector) {
  38783. labels = labels.concat(collector());
  38784. });
  38785. (this.yAxis || []).forEach(function (yAxis) {
  38786. if (yAxis.options.stackLabels &&
  38787. !yAxis.options.stackLabels.allowOverlap) {
  38788. objectEach(yAxis.stacks, function (stack) {
  38789. objectEach(stack, function (stackItem) {
  38790. labels.push(stackItem.label);
  38791. });
  38792. });
  38793. }
  38794. });
  38795. (this.series || []).forEach(function (series) {
  38796. var dlOptions = series.options.dataLabels;
  38797. if (series.visible &&
  38798. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  38799. series.points.forEach(function (point) {
  38800. if (point.visible) {
  38801. var dataLabels = (isArray(point.dataLabels) ?
  38802. point.dataLabels :
  38803. (point.dataLabel ? [point.dataLabel] : []));
  38804. dataLabels.forEach(function (label) {
  38805. var options = label.options;
  38806. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  38807. if (!options.allowOverlap) {
  38808. labels.push(label);
  38809. }
  38810. });
  38811. }
  38812. });
  38813. }
  38814. });
  38815. this.hideOverlappingLabels(labels);
  38816. });
  38817. /**
  38818. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  38819. * provide a smooth visual imression.
  38820. *
  38821. * @private
  38822. * @function Highcharts.Chart#hideOverlappingLabels
  38823. * @param {Array<Highcharts.SVGElement>} labels
  38824. * Rendered data labels
  38825. * @return {void}
  38826. * @requires modules/overlapping-datalabels
  38827. */
  38828. Chart.prototype.hideOverlappingLabels = function (labels) {
  38829. var chart = this, len = labels.length, ren = chart.renderer, label, i, j, label1, label2, box1, box2, isLabelAffected = false, isIntersectRect = function (box1, box2) {
  38830. return !(box2.x > box1.x + box1.width ||
  38831. box2.x + box2.width < box1.x ||
  38832. box2.y > box1.y + box1.height ||
  38833. box2.y + box2.height < box1.y);
  38834. },
  38835. // Get the box with its position inside the chart, as opposed to getBBox
  38836. // that only reports the position relative to the parent.
  38837. getAbsoluteBox = function (label) {
  38838. var pos, parent, bBox,
  38839. // Substract the padding if no background or border (#4333)
  38840. padding = label.box ? 0 : (label.padding || 0), lineHeightCorrection = 0;
  38841. if (label &&
  38842. (!label.alignAttr || label.placed)) {
  38843. pos = label.alignAttr || {
  38844. x: label.attr('x'),
  38845. y: label.attr('y')
  38846. };
  38847. parent = label.parentGroup;
  38848. // Get width and height if pure text nodes (stack labels)
  38849. if (!label.width) {
  38850. bBox = label.getBBox();
  38851. label.width = bBox.width;
  38852. label.height = bBox.height;
  38853. // Labels positions are computed from top left corner, so
  38854. // we need to substract the text height from text nodes too.
  38855. lineHeightCorrection = ren
  38856. .fontMetrics(null, label.element).h;
  38857. }
  38858. return {
  38859. x: pos.x + (parent.translateX || 0) + padding,
  38860. y: pos.y + (parent.translateY || 0) + padding -
  38861. lineHeightCorrection,
  38862. width: label.width - 2 * padding,
  38863. height: label.height - 2 * padding
  38864. };
  38865. }
  38866. };
  38867. for (i = 0; i < len; i++) {
  38868. label = labels[i];
  38869. if (label) {
  38870. // Mark with initial opacity
  38871. label.oldOpacity = label.opacity;
  38872. label.newOpacity = 1;
  38873. label.absoluteBox = getAbsoluteBox(label);
  38874. }
  38875. }
  38876. // Prevent a situation in a gradually rising slope, that each label will
  38877. // hide the previous one because the previous one always has lower rank.
  38878. labels.sort(function (a, b) {
  38879. return (b.labelrank || 0) - (a.labelrank || 0);
  38880. });
  38881. // Detect overlapping labels
  38882. for (i = 0; i < len; i++) {
  38883. label1 = labels[i];
  38884. box1 = label1 && label1.absoluteBox;
  38885. for (j = i + 1; j < len; ++j) {
  38886. label2 = labels[j];
  38887. box2 = label2 && label2.absoluteBox;
  38888. if (box1 &&
  38889. box2 &&
  38890. label1 !== label2 && // #6465, polar chart with connectEnds
  38891. label1.newOpacity !== 0 &&
  38892. label2.newOpacity !== 0) {
  38893. if (isIntersectRect(box1, box2)) {
  38894. (label1.labelrank < label2.labelrank ? label1 : label2)
  38895. .newOpacity = 0;
  38896. }
  38897. }
  38898. }
  38899. }
  38900. // Hide or show
  38901. labels.forEach(function (label) {
  38902. var complete, newOpacity;
  38903. if (label) {
  38904. newOpacity = label.newOpacity;
  38905. if (label.oldOpacity !== newOpacity) {
  38906. // Make sure the label is completely hidden to avoid catching
  38907. // clicks (#4362)
  38908. if (label.alignAttr && label.placed) { // data labels
  38909. if (newOpacity) {
  38910. label.show(true);
  38911. }
  38912. else {
  38913. complete = function () {
  38914. label.hide(true);
  38915. label.placed = false; // avoid animation from top
  38916. };
  38917. }
  38918. isLabelAffected = true;
  38919. // Animate or set the opacity
  38920. label.alignAttr.opacity = newOpacity;
  38921. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  38922. fireEvent(chart, 'afterHideOverlappingLabel');
  38923. }
  38924. else { // other labels, tick labels
  38925. label.attr({
  38926. opacity: newOpacity
  38927. });
  38928. }
  38929. }
  38930. label.isOld = true;
  38931. }
  38932. });
  38933. if (isLabelAffected) {
  38934. fireEvent(chart, 'afterHideAllOverlappingLabels');
  38935. }
  38936. };
  38937. });
  38938. _registerModule(_modules, 'parts/Interaction.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  38939. /* *
  38940. *
  38941. * (c) 2010-2019 Torstein Honsi
  38942. *
  38943. * License: www.highcharts.com/license
  38944. *
  38945. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38946. *
  38947. * */
  38948. /**
  38949. * @interface Highcharts.PointEventsOptionsObject
  38950. */ /**
  38951. * Fires when the point is selected either programmatically or following a click
  38952. * on the point. One parameter, `event`, is passed to the function. Returning
  38953. * `false` cancels the operation.
  38954. * @name Highcharts.PointEventsOptionsObject#select
  38955. * @type {Highcharts.PointSelectCallbackFunction|undefined}
  38956. */ /**
  38957. * Fires when the point is unselected either programmatically or following a
  38958. * click on the point. One parameter, `event`, is passed to the function.
  38959. * Returning `false` cancels the operation.
  38960. * @name Highcharts.PointEventsOptionsObject#unselect
  38961. * @type {Highcharts.PointUnselectCallbackFunction|undefined}
  38962. */
  38963. /**
  38964. * Information about the select/unselect event.
  38965. *
  38966. * @interface Highcharts.PointInteractionEventObject
  38967. * @extends global.Event
  38968. */ /**
  38969. * @name Highcharts.PointInteractionEventObject#accumulate
  38970. * @type {boolean}
  38971. */
  38972. /**
  38973. * Gets fired when the point is selected either programmatically or following a
  38974. * click on the point.
  38975. *
  38976. * @callback Highcharts.PointSelectCallbackFunction
  38977. *
  38978. * @param {Highcharts.Point} this
  38979. * Point where the event occured.
  38980. *
  38981. * @param {Highcharts.PointInteractionEventObject} event
  38982. * Event that occured.
  38983. */
  38984. /**
  38985. * Fires when the point is unselected either programmatically or following a
  38986. * click on the point.
  38987. *
  38988. * @callback Highcharts.PointUnselectCallbackFunction
  38989. *
  38990. * @param {Highcharts.Point} this
  38991. * Point where the event occured.
  38992. *
  38993. * @param {Highcharts.PointInteractionEventObject} event
  38994. * Event that occured.
  38995. */
  38996. var defined = U.defined, extend = U.extend, isArray = U.isArray, isObject = U.isObject, objectEach = U.objectEach, pick = U.pick;
  38997. var addEvent = H.addEvent, Chart = H.Chart, createElement = H.createElement, css = H.css, defaultOptions = H.defaultOptions, defaultPlotOptions = H.defaultPlotOptions, fireEvent = H.fireEvent, hasTouch = H.hasTouch, Legend = H.Legend, merge = H.merge, Point = H.Point, Series = H.Series, seriesTypes = H.seriesTypes, svg = H.svg, TrackerMixin;
  38998. /* eslint-disable valid-jsdoc */
  38999. /**
  39000. * TrackerMixin for points and graphs.
  39001. *
  39002. * @private
  39003. * @mixin Highcharts.TrackerMixin
  39004. */
  39005. TrackerMixin = H.TrackerMixin = {
  39006. /**
  39007. * Draw the tracker for a point.
  39008. *
  39009. * @private
  39010. * @function Highcharts.TrackerMixin.drawTrackerPoint
  39011. * @param {Highcharts.Series} this
  39012. * @return {void}
  39013. * @fires Highcharts.Series#event:afterDrawTracker
  39014. */
  39015. drawTrackerPoint: function () {
  39016. var series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) {
  39017. var point = pointer.getPointFromEvent(e);
  39018. // undefined on graph in scatterchart
  39019. if (typeof point !== 'undefined') {
  39020. pointer.isDirectTouch = true;
  39021. point.onMouseOver(e);
  39022. }
  39023. }, dataLabels;
  39024. // Add reference to the point
  39025. series.points.forEach(function (point) {
  39026. dataLabels = (isArray(point.dataLabels) ?
  39027. point.dataLabels :
  39028. (point.dataLabel ? [point.dataLabel] : []));
  39029. if (point.graphic) {
  39030. point.graphic.element.point = point;
  39031. }
  39032. dataLabels.forEach(function (dataLabel) {
  39033. if (dataLabel.div) {
  39034. dataLabel.div.point = point;
  39035. }
  39036. else {
  39037. dataLabel.element.point = point;
  39038. }
  39039. });
  39040. });
  39041. // Add the event listeners, we need to do this only once
  39042. if (!series._hasTracking) {
  39043. series.trackerGroups.forEach(function (key) {
  39044. if (series[key]) {
  39045. // we don't always have dataLabelsGroup
  39046. series[key]
  39047. .addClass('highcharts-tracker')
  39048. .on('mouseover', onMouseOver)
  39049. .on('mouseout', function (e) {
  39050. pointer.onTrackerMouseOut(e);
  39051. });
  39052. if (hasTouch) {
  39053. series[key].on('touchstart', onMouseOver);
  39054. }
  39055. if (!chart.styledMode && series.options.cursor) {
  39056. series[key]
  39057. .css(css)
  39058. .css({ cursor: series.options.cursor });
  39059. }
  39060. }
  39061. });
  39062. series._hasTracking = true;
  39063. }
  39064. fireEvent(this, 'afterDrawTracker');
  39065. },
  39066. /**
  39067. * Draw the tracker object that sits above all data labels and markers to
  39068. * track mouse events on the graph or points. For the line type charts
  39069. * the tracker uses the same graphPath, but with a greater stroke width
  39070. * for better control.
  39071. *
  39072. * @private
  39073. * @function Highcharts.TrackerMixin.drawTrackerGraph
  39074. * @param {Highcharts.Series} this
  39075. * @return {void}
  39076. * @fires Highcharts.Series#event:afterDrawTracker
  39077. */
  39078. drawTrackerGraph: function () {
  39079. var series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat(trackByArea ?
  39080. series.areaPath :
  39081. series.graphPath), trackerPathLength = trackerPath.length, chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, i, onMouseOver = function () {
  39082. if (chart.hoverSeries !== series) {
  39083. series.onMouseOver();
  39084. }
  39085. },
  39086. /*
  39087. * Empirical lowest possible opacities for TRACKER_FILL for an
  39088. * element to stay invisible but clickable
  39089. * IE6: 0.002
  39090. * IE7: 0.002
  39091. * IE8: 0.002
  39092. * IE9: 0.00000000001 (unlimited)
  39093. * IE10: 0.0001 (exporting only)
  39094. * FF: 0.00000000001 (unlimited)
  39095. * Chrome: 0.000001
  39096. * Safari: 0.000001
  39097. * Opera: 0.00000000001 (unlimited)
  39098. */
  39099. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  39100. // Extend end points. A better way would be to use round linecaps,
  39101. // but those are not clickable in VML.
  39102. if (trackerPathLength && !trackByArea) {
  39103. i = trackerPathLength + 1;
  39104. while (i--) {
  39105. if (trackerPath[i] === 'M') {
  39106. // extend left side
  39107. trackerPath.splice(i + 1, 0, trackerPath[i + 1] - snap, trackerPath[i + 2], 'L');
  39108. }
  39109. if ((i && trackerPath[i] === 'M') ||
  39110. i === trackerPathLength) {
  39111. // extend right side
  39112. trackerPath.splice(i, 0, 'L', trackerPath[i - 2] + snap, trackerPath[i - 1]);
  39113. }
  39114. }
  39115. }
  39116. // draw the tracker
  39117. if (tracker) {
  39118. tracker.attr({ d: trackerPath });
  39119. }
  39120. else if (series.graph) { // create
  39121. series.tracker = renderer.path(trackerPath)
  39122. .attr({
  39123. visibility: series.visible ? 'visible' : 'hidden',
  39124. zIndex: 2
  39125. })
  39126. .addClass(trackByArea ?
  39127. 'highcharts-tracker-area' :
  39128. 'highcharts-tracker-line')
  39129. .add(series.group);
  39130. if (!chart.styledMode) {
  39131. series.tracker.attr({
  39132. 'stroke-linejoin': 'round',
  39133. stroke: TRACKER_FILL,
  39134. fill: trackByArea ? TRACKER_FILL : 'none',
  39135. 'stroke-width': series.graph.strokeWidth() +
  39136. (trackByArea ? 0 : 2 * snap)
  39137. });
  39138. }
  39139. // The tracker is added to the series group, which is clipped, but
  39140. // is covered by the marker group. So the marker group also needs to
  39141. // capture events.
  39142. [series.tracker, series.markerGroup].forEach(function (tracker) {
  39143. tracker.addClass('highcharts-tracker')
  39144. .on('mouseover', onMouseOver)
  39145. .on('mouseout', function (e) {
  39146. pointer.onTrackerMouseOut(e);
  39147. });
  39148. if (options.cursor && !chart.styledMode) {
  39149. tracker.css({ cursor: options.cursor });
  39150. }
  39151. if (hasTouch) {
  39152. tracker.on('touchstart', onMouseOver);
  39153. }
  39154. });
  39155. }
  39156. fireEvent(this, 'afterDrawTracker');
  39157. }
  39158. };
  39159. /* End TrackerMixin */
  39160. // Add tracking event listener to the series group, so the point graphics
  39161. // themselves act as trackers
  39162. if (seriesTypes.column) {
  39163. /**
  39164. * @private
  39165. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.column#drawTracker
  39166. */
  39167. seriesTypes.column.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39168. }
  39169. if (seriesTypes.pie) {
  39170. /**
  39171. * @private
  39172. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.pie#drawTracker
  39173. */
  39174. seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39175. }
  39176. if (seriesTypes.scatter) {
  39177. /**
  39178. * @private
  39179. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.scatter#drawTracker
  39180. */
  39181. seriesTypes.scatter.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39182. }
  39183. // Extend Legend for item events.
  39184. extend(Legend.prototype, {
  39185. /**
  39186. * @private
  39187. * @function Highcharts.Legend#setItemEvents
  39188. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  39189. * @param {Highcharts.SVGElement} legendItem
  39190. * @param {boolean} [useHTML=false]
  39191. * @return {void}
  39192. * @fires Highcharts.Point#event:legendItemClick
  39193. * @fires Highcharts.Series#event:legendItemClick
  39194. */
  39195. setItemEvents: function (item, legendItem, useHTML) {
  39196. var legend = this, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, activeClass = 'highcharts-legend-' +
  39197. (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode;
  39198. // Set the events on the item group, or in case of useHTML, the item
  39199. // itself (#1249)
  39200. (useHTML ? legendItem : item.legendGroup)
  39201. .on('mouseover', function () {
  39202. if (item.visible) {
  39203. legend.allItems.forEach(function (inactiveItem) {
  39204. if (item !== inactiveItem) {
  39205. inactiveItem.setState('inactive', !isPoint);
  39206. }
  39207. });
  39208. }
  39209. item.setState('hover');
  39210. // A CSS class to dim or hide other than the hovered series.
  39211. // Works only if hovered series is visible (#10071).
  39212. if (item.visible) {
  39213. boxWrapper.addClass(activeClass);
  39214. }
  39215. if (!styledMode) {
  39216. legendItem.css(legend.options.itemHoverStyle);
  39217. }
  39218. })
  39219. .on('mouseout', function () {
  39220. if (!legend.chart.styledMode) {
  39221. legendItem.css(merge(item.visible ?
  39222. legend.itemStyle :
  39223. legend.itemHiddenStyle));
  39224. }
  39225. legend.allItems.forEach(function (inactiveItem) {
  39226. if (item !== inactiveItem) {
  39227. inactiveItem.setState('', !isPoint);
  39228. }
  39229. });
  39230. // A CSS class to dim or hide other than the hovered series
  39231. boxWrapper.removeClass(activeClass);
  39232. item.setState();
  39233. })
  39234. .on('click', function (event) {
  39235. var strLegendItemClick = 'legendItemClick', fnLegendItemClick = function () {
  39236. if (item.setVisible) {
  39237. item.setVisible();
  39238. }
  39239. // Reset inactive state
  39240. legend.allItems.forEach(function (inactiveItem) {
  39241. if (item !== inactiveItem) {
  39242. inactiveItem.setState(item.visible ? 'inactive' : '', !isPoint);
  39243. }
  39244. });
  39245. };
  39246. // A CSS class to dim or hide other than the hovered series.
  39247. // Event handling in iOS causes the activeClass to be added
  39248. // prior to click in some cases (#7418).
  39249. boxWrapper.removeClass(activeClass);
  39250. // Pass over the click/touch event. #4.
  39251. event = {
  39252. browserEvent: event
  39253. };
  39254. // click the name or symbol
  39255. if (item.firePointEvent) { // point
  39256. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  39257. }
  39258. else {
  39259. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  39260. }
  39261. });
  39262. },
  39263. /**
  39264. * @private
  39265. * @function Highcharts.Legend#createCheckboxForItem
  39266. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  39267. * @return {void}
  39268. * @fires Highcharts.Series#event:checkboxClick
  39269. */
  39270. createCheckboxForItem: function (item) {
  39271. var legend = this;
  39272. item.checkbox = createElement('input', {
  39273. type: 'checkbox',
  39274. className: 'highcharts-legend-checkbox',
  39275. checked: item.selected,
  39276. defaultChecked: item.selected // required by IE7
  39277. }, legend.options.itemCheckboxStyle, legend.chart.container);
  39278. addEvent(item.checkbox, 'click', function (event) {
  39279. var target = event.target;
  39280. fireEvent(item.series || item, 'checkboxClick', {
  39281. checked: target.checked,
  39282. item: item
  39283. }, function () {
  39284. item.select();
  39285. });
  39286. });
  39287. }
  39288. });
  39289. // Extend the Chart object with interaction
  39290. extend(Chart.prototype, /** @lends Chart.prototype */ {
  39291. /**
  39292. * Display the zoom button.
  39293. *
  39294. * @private
  39295. * @function Highcharts.Chart#showResetZoom
  39296. * @return {void}
  39297. * @fires Highcharts.Chart#event:beforeShowResetZoom
  39298. */
  39299. showResetZoom: function () {
  39300. var chart = this, lang = defaultOptions.lang, btnOptions = chart.options.chart.resetZoomButton, theme = btnOptions.theme, states = theme.states, alignTo = (btnOptions.relativeTo === 'chart' ||
  39301. btnOptions.relativeTo === 'spaceBox' ?
  39302. null :
  39303. 'plotBox');
  39304. /**
  39305. * @private
  39306. */
  39307. function zoomOut() {
  39308. chart.zoomOut();
  39309. }
  39310. fireEvent(this, 'beforeShowResetZoom', null, function () {
  39311. chart.resetZoomButton = chart.renderer
  39312. .button(lang.resetZoom, null, null, zoomOut, theme, states && states.hover)
  39313. .attr({
  39314. align: btnOptions.position.align,
  39315. title: lang.resetZoomTitle
  39316. })
  39317. .addClass('highcharts-reset-zoom')
  39318. .add()
  39319. .align(btnOptions.position, false, alignTo);
  39320. });
  39321. fireEvent(this, 'afterShowResetZoom');
  39322. },
  39323. /**
  39324. * Zoom the chart out after a user has zoomed in. See also
  39325. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  39326. *
  39327. * @function Highcharts.Chart#zoomOut
  39328. * @return {void}
  39329. * @fires Highcharts.Chart#event:selection
  39330. */
  39331. zoomOut: function () {
  39332. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  39333. },
  39334. /**
  39335. * Zoom into a given portion of the chart given by axis coordinates.
  39336. *
  39337. * @private
  39338. * @function Highcharts.Chart#zoom
  39339. * @param {Highcharts.SelectEventObject} event
  39340. * @return {void}
  39341. */
  39342. zoom: function (event) {
  39343. var chart = this, hasZoomed, pointer = chart.pointer, displayButton = false, mouseDownPos = chart.inverted ? pointer.mouseDownX : pointer.mouseDownY, resetZoomButton;
  39344. // If zoom is called with no arguments, reset the axes
  39345. if (!event || event.resetSelection) {
  39346. chart.axes.forEach(function (axis) {
  39347. hasZoomed = axis.zoom();
  39348. });
  39349. pointer.initiated = false; // #6804
  39350. }
  39351. else { // else, zoom in on all axes
  39352. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  39353. var axis = axisData.axis, axisStartPos = chart.inverted ? axis.left : axis.top, axisEndPos = chart.inverted ?
  39354. axisStartPos + axis.width : axisStartPos + axis.height, isXAxis = axis.isXAxis, isWithinPane = false;
  39355. // Check if zoomed area is within the pane (#1289).
  39356. // In case of multiple panes only one pane should be zoomed.
  39357. if ((!isXAxis &&
  39358. mouseDownPos >= axisStartPos &&
  39359. mouseDownPos <= axisEndPos) ||
  39360. isXAxis ||
  39361. !defined(mouseDownPos)) {
  39362. isWithinPane = true;
  39363. }
  39364. // don't zoom more than minRange
  39365. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] && isWithinPane) {
  39366. hasZoomed = axis.zoom(axisData.min, axisData.max);
  39367. if (axis.displayBtn) {
  39368. displayButton = true;
  39369. }
  39370. }
  39371. });
  39372. }
  39373. // Show or hide the Reset zoom button
  39374. resetZoomButton = chart.resetZoomButton;
  39375. if (displayButton && !resetZoomButton) {
  39376. chart.showResetZoom();
  39377. }
  39378. else if (!displayButton && isObject(resetZoomButton)) {
  39379. chart.resetZoomButton = resetZoomButton.destroy();
  39380. }
  39381. // Redraw
  39382. if (hasZoomed) {
  39383. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  39384. }
  39385. },
  39386. /**
  39387. * Pan the chart by dragging the mouse across the pane. This function is
  39388. * called on mouse move, and the distance to pan is computed from chartX
  39389. * compared to the first chartX position in the dragging operation.
  39390. *
  39391. * @private
  39392. * @function Highcharts.Chart#pan
  39393. * @param {Highcharts.PointerEventObject} e
  39394. * @param {string} panning
  39395. * @return {void}
  39396. */
  39397. pan: function (e, panning) {
  39398. var chart = this, hoverPoints = chart.hoverPoints, panningOptions, chartOptions = chart.options.chart, doRedraw, type;
  39399. if (typeof panning === 'object') {
  39400. panningOptions = panning;
  39401. }
  39402. else {
  39403. panningOptions = {
  39404. enabled: panning,
  39405. type: 'x'
  39406. };
  39407. }
  39408. if (chartOptions && chartOptions.panning) {
  39409. chartOptions.panning = panningOptions;
  39410. }
  39411. type = panningOptions.type;
  39412. fireEvent(this, 'pan', { originalEvent: e }, function () {
  39413. // remove active points for shared tooltip
  39414. if (hoverPoints) {
  39415. hoverPoints.forEach(function (point) {
  39416. point.setState();
  39417. });
  39418. }
  39419. // panning axis mapping
  39420. var xy = [1]; // x
  39421. if (type === 'xy') {
  39422. xy = [1, 0];
  39423. }
  39424. else if (type === 'y') {
  39425. xy = [0];
  39426. }
  39427. xy.forEach(function (isX) {
  39428. var axis = chart[isX ? 'xAxis' : 'yAxis'][0], axisOpt = axis.options, 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) ||
  39429. (!axis.reversed && chart.inverted) ?
  39430. -1 :
  39431. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  39432. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  39433. halfPointRange * pointRangeDirection, flipped = panMax < panMin, newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, paddedMin = Math.min(extremes.dataMin, halfPointRange ?
  39434. extremes.min :
  39435. axis.toValue(axis.toPixels(extremes.min) -
  39436. axis.minPixelPadding)), paddedMax = Math.max(extremes.dataMax, halfPointRange ?
  39437. extremes.max :
  39438. axis.toValue(axis.toPixels(extremes.max) +
  39439. axis.minPixelPadding)), spill;
  39440. // It is not necessary to calculate extremes on ordinal axis,
  39441. // because the are already calculated, so we don't want to
  39442. // override them.
  39443. if (!axisOpt.ordinal) {
  39444. // If the new range spills over, either to the min or max,
  39445. // adjust the new range.
  39446. if (isX) {
  39447. spill = paddedMin - newMin;
  39448. if (spill > 0) {
  39449. newMax += spill;
  39450. newMin = paddedMin;
  39451. }
  39452. spill = newMax - paddedMax;
  39453. if (spill > 0) {
  39454. newMax = paddedMax;
  39455. newMin -= spill;
  39456. }
  39457. }
  39458. // Set new extremes if they are actually new
  39459. if (axis.series.length &&
  39460. newMin !== extremes.min &&
  39461. newMax !== extremes.max &&
  39462. isX ? true : (axis.panningState &&
  39463. newMin >= axis.panningState
  39464. .startMin &&
  39465. newMax <= axis.panningState
  39466. .startMax //
  39467. )) {
  39468. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  39469. doRedraw = true;
  39470. }
  39471. // set new reference for next run:
  39472. chart[mouseDown] = mousePos;
  39473. }
  39474. });
  39475. if (doRedraw) {
  39476. chart.redraw(false);
  39477. }
  39478. css(chart.container, { cursor: 'move' });
  39479. });
  39480. }
  39481. });
  39482. // Extend the Point object with interaction
  39483. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  39484. /**
  39485. * Toggle the selection status of a point.
  39486. *
  39487. * @see Highcharts.Chart#getSelectedPoints
  39488. *
  39489. * @sample highcharts/members/point-select/
  39490. * Select a point from a button
  39491. * @sample highcharts/chart/events-selection-points/
  39492. * Select a range of points through a drag selection
  39493. * @sample maps/series/data-id/
  39494. * Select a point in Highmaps
  39495. *
  39496. * @function Highcharts.Point#select
  39497. *
  39498. * @param {boolean} [selected]
  39499. * When `true`, the point is selected. When `false`, the point is
  39500. * unselected. When `null` or `undefined`, the selection state is
  39501. * toggled.
  39502. *
  39503. * @param {boolean} [accumulate=false]
  39504. * When `true`, the selection is added to other selected points.
  39505. * When `false`, other selected points are deselected. Internally in
  39506. * Highcharts, when
  39507. * [allowPointSelect](http://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  39508. * is `true`, selected points are accumulated on Control, Shift or
  39509. * Cmd clicking the point.
  39510. *
  39511. * @return {void}
  39512. *
  39513. * @fires Highcharts.Point#event:select
  39514. * @fires Highcharts.Point#event:unselect
  39515. */
  39516. select: function (selected, accumulate) {
  39517. var point = this, series = point.series, chart = series.chart;
  39518. selected = pick(selected, !point.selected);
  39519. this.selectedStaging = selected;
  39520. // fire the event with the default handler
  39521. point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
  39522. /**
  39523. * Whether the point is selected or not.
  39524. *
  39525. * @see Point#select
  39526. * @see Chart#getSelectedPoints
  39527. *
  39528. * @name Highcharts.Point#selected
  39529. * @type {boolean}
  39530. */
  39531. point.selected = point.options.selected = selected;
  39532. series.options.data[series.data.indexOf(point)] =
  39533. point.options;
  39534. point.setState(selected && 'select');
  39535. // unselect all other points unless Ctrl or Cmd + click
  39536. if (!accumulate) {
  39537. chart.getSelectedPoints().forEach(function (loopPoint) {
  39538. var loopSeries = loopPoint.series;
  39539. if (loopPoint.selected && loopPoint !== point) {
  39540. loopPoint.selected = loopPoint.options.selected =
  39541. false;
  39542. loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
  39543. // Programatically selecting a point should restore
  39544. // normal state, but when click happened on other
  39545. // point, set inactive state to match other points
  39546. loopPoint.setState(chart.hoverPoints &&
  39547. loopSeries.options.inactiveOtherPoints ?
  39548. 'inactive' : '');
  39549. loopPoint.firePointEvent('unselect');
  39550. }
  39551. });
  39552. }
  39553. });
  39554. delete this.selectedStaging;
  39555. },
  39556. /**
  39557. * Runs on mouse over the point. Called internally from mouse and touch
  39558. * events.
  39559. *
  39560. * @function Highcharts.Point#onMouseOver
  39561. *
  39562. * @param {Highcharts.PointerEventObject} [e]
  39563. * The event arguments.
  39564. *
  39565. * @return {void}
  39566. */
  39567. onMouseOver: function (e) {
  39568. var point = this, series = point.series, chart = series.chart, pointer = chart.pointer;
  39569. e = e ?
  39570. pointer.normalize(e) :
  39571. // In cases where onMouseOver is called directly without an event
  39572. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  39573. pointer.runPointActions(e, point);
  39574. },
  39575. /**
  39576. * Runs on mouse out from the point. Called internally from mouse and touch
  39577. * events.
  39578. *
  39579. * @function Highcharts.Point#onMouseOut
  39580. * @return {void}
  39581. * @fires Highcharts.Point#event:mouseOut
  39582. */
  39583. onMouseOut: function () {
  39584. var point = this, chart = point.series.chart;
  39585. point.firePointEvent('mouseOut');
  39586. if (!point.series.options.inactiveOtherPoints) {
  39587. (chart.hoverPoints || []).forEach(function (p) {
  39588. p.setState();
  39589. });
  39590. }
  39591. chart.hoverPoints = chart.hoverPoint = null;
  39592. },
  39593. /**
  39594. * Import events from the series' and point's options. Only do it on
  39595. * demand, to save processing time on hovering.
  39596. *
  39597. * @private
  39598. * @function Highcharts.Point#importEvents
  39599. * @return {void}
  39600. */
  39601. importEvents: function () {
  39602. if (!this.hasImportedEvents) {
  39603. var point = this, options = merge(point.series.options.point, point.options), events = options.events;
  39604. point.events = events;
  39605. objectEach(events, function (event, eventType) {
  39606. if (H.isFunction(event)) {
  39607. addEvent(point, eventType, event);
  39608. }
  39609. });
  39610. this.hasImportedEvents = true;
  39611. }
  39612. },
  39613. /**
  39614. * Set the point's state.
  39615. *
  39616. * @function Highcharts.Point#setState
  39617. *
  39618. * @param {Highcharts.PointStateValue|""} [state]
  39619. * The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
  39620. * or `''` (an empty string), `'normal'` or `undefined` to set to
  39621. * normal state.
  39622. * @param {boolean} [move]
  39623. * State for animation.
  39624. *
  39625. * @fires Highcharts.Point#event:afterSetState
  39626. */
  39627. setState: function (state, move) {
  39628. var point = this, series = point.series, previousState = point.state, stateOptions = (series.options.states[state || 'normal'] ||
  39629. {}), markerOptions = (defaultPlotOptions[series.type].marker &&
  39630. series.options.marker), normalDisabled = (markerOptions && markerOptions.enabled === false), markerStateOptions = ((markerOptions &&
  39631. markerOptions.states &&
  39632. 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;
  39633. state = state || ''; // empty string
  39634. if (
  39635. // already has this state
  39636. (state === point.state && !move) ||
  39637. // selected points don't respond to hover
  39638. (point.selected && state !== 'select') ||
  39639. // series' state options is disabled
  39640. (stateOptions.enabled === false) ||
  39641. // general point marker's state options is disabled
  39642. (state && (stateDisabled ||
  39643. (normalDisabled &&
  39644. markerStateOptions.enabled === false))) ||
  39645. // individual point marker's state options is disabled
  39646. (state &&
  39647. pointMarker.states &&
  39648. pointMarker.states[state] &&
  39649. pointMarker.states[state].enabled === false) // #1610
  39650. ) {
  39651. return;
  39652. }
  39653. point.state = state;
  39654. if (hasMarkers) {
  39655. markerAttribs = series.markerAttribs(point, state);
  39656. }
  39657. // Apply hover styles to the existing point
  39658. if (point.graphic) {
  39659. if (previousState) {
  39660. point.graphic.removeClass('highcharts-point-' + previousState);
  39661. }
  39662. if (state) {
  39663. point.graphic.addClass('highcharts-point-' + state);
  39664. }
  39665. if (!chart.styledMode) {
  39666. pointAttribs = series.pointAttribs(point, state);
  39667. pointAttribsAnimation = pick(chart.options.chart.animation, stateOptions.animation);
  39668. // Some inactive points (e.g. slices in pie) should apply
  39669. // oppacity also for it's labels
  39670. if (series.options.inactiveOtherPoints) {
  39671. (point.dataLabels || []).forEach(function (label) {
  39672. if (label) {
  39673. label.animate({
  39674. opacity: pointAttribs.opacity
  39675. }, pointAttribsAnimation);
  39676. }
  39677. });
  39678. if (point.connector) {
  39679. point.connector.animate({
  39680. opacity: pointAttribs.opacity
  39681. }, pointAttribsAnimation);
  39682. }
  39683. }
  39684. point.graphic.animate(pointAttribs, pointAttribsAnimation);
  39685. }
  39686. if (markerAttribs) {
  39687. point.graphic.animate(markerAttribs, pick(
  39688. // Turn off globally:
  39689. chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
  39690. }
  39691. // Zooming in from a range with no markers to a range with markers
  39692. if (stateMarkerGraphic) {
  39693. stateMarkerGraphic.hide();
  39694. }
  39695. }
  39696. else {
  39697. // if a graphic is not applied to each point in the normal state,
  39698. // create a shared graphic for the hover state
  39699. if (state && markerStateOptions) {
  39700. newSymbol = pointMarker.symbol || series.symbol;
  39701. // If the point has another symbol than the previous one, throw
  39702. // away the state marker graphic and force a new one (#1459)
  39703. if (stateMarkerGraphic &&
  39704. stateMarkerGraphic.currentSymbol !== newSymbol) {
  39705. stateMarkerGraphic = stateMarkerGraphic.destroy();
  39706. }
  39707. // Add a new state marker graphic
  39708. if (markerAttribs) {
  39709. if (!stateMarkerGraphic) {
  39710. if (newSymbol) {
  39711. series.stateMarkerGraphic = stateMarkerGraphic =
  39712. chart.renderer
  39713. .symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height)
  39714. .add(series.markerGroup);
  39715. stateMarkerGraphic.currentSymbol = newSymbol;
  39716. }
  39717. // Move the existing graphic
  39718. }
  39719. else {
  39720. stateMarkerGraphic[move ? 'animate' : 'attr']({
  39721. x: markerAttribs.x,
  39722. y: markerAttribs.y
  39723. });
  39724. }
  39725. }
  39726. if (!chart.styledMode && stateMarkerGraphic) {
  39727. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  39728. }
  39729. }
  39730. if (stateMarkerGraphic) {
  39731. stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
  39732. stateMarkerGraphic.element.point = point; // #4310
  39733. }
  39734. }
  39735. // Show me your halo
  39736. haloOptions = stateOptions.halo;
  39737. var markerGraphic = (point.graphic || stateMarkerGraphic);
  39738. var markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
  39739. if (haloOptions &&
  39740. haloOptions.size &&
  39741. markerGraphic &&
  39742. markerVisibility !== 'hidden' &&
  39743. !point.isCluster) {
  39744. if (!halo) {
  39745. series.halo = halo = chart.renderer.path()
  39746. // #5818, #5903, #6705
  39747. .add(markerGraphic.parentGroup);
  39748. }
  39749. halo.show()[move ? 'animate' : 'attr']({
  39750. d: point.haloPath(haloOptions.size)
  39751. });
  39752. halo.attr({
  39753. 'class': 'highcharts-halo highcharts-color-' +
  39754. pick(point.colorIndex, series.colorIndex) +
  39755. (point.className ? ' ' + point.className : ''),
  39756. 'visibility': markerVisibility,
  39757. 'zIndex': -1 // #4929, #8276
  39758. });
  39759. halo.point = point; // #6055
  39760. if (!chart.styledMode) {
  39761. halo.attr(extend({
  39762. 'fill': point.color || series.color,
  39763. 'fill-opacity': haloOptions.opacity
  39764. }, haloOptions.attributes));
  39765. }
  39766. }
  39767. else if (halo && halo.point && halo.point.haloPath) {
  39768. // Animate back to 0 on the current halo point (#6055)
  39769. halo.animate({ d: halo.point.haloPath(0) }, null,
  39770. // Hide after unhovering. The `complete` callback runs in the
  39771. // halo's context (#7681).
  39772. halo.hide);
  39773. }
  39774. fireEvent(point, 'afterSetState');
  39775. },
  39776. /**
  39777. * Get the path definition for the halo, which is usually a shadow-like
  39778. * circle around the currently hovered point.
  39779. *
  39780. * @function Highcharts.Point#haloPath
  39781. *
  39782. * @param {number} size
  39783. * The radius of the circular halo.
  39784. *
  39785. * @return {Highcharts.SVGElement}
  39786. * The path definition.
  39787. */
  39788. haloPath: function (size) {
  39789. var series = this.series, chart = series.chart;
  39790. return chart.renderer.symbols.circle(Math.floor(this.plotX) - size, this.plotY - size, size * 2, size * 2);
  39791. }
  39792. });
  39793. // Extend the Series object with interaction
  39794. extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
  39795. /**
  39796. * Runs on mouse over the series graphical items.
  39797. *
  39798. * @function Highcharts.Series#onMouseOver
  39799. * @return {void}
  39800. * @fires Highcharts.Series#event:mouseOver
  39801. */
  39802. onMouseOver: function () {
  39803. var series = this, chart = series.chart, hoverSeries = chart.hoverSeries;
  39804. // set normal state to previous series
  39805. if (hoverSeries && hoverSeries !== series) {
  39806. hoverSeries.onMouseOut();
  39807. }
  39808. // trigger the event, but to save processing time,
  39809. // only if defined
  39810. if (series.options.events.mouseOver) {
  39811. fireEvent(series, 'mouseOver');
  39812. }
  39813. // hover this
  39814. series.setState('hover');
  39815. /**
  39816. * Contains the original hovered series.
  39817. *
  39818. * @name Highcharts.Chart#hoverSeries
  39819. * @type {Highcharts.Series|null}
  39820. */
  39821. chart.hoverSeries = series;
  39822. },
  39823. /**
  39824. * Runs on mouse out of the series graphical items.
  39825. *
  39826. * @function Highcharts.Series#onMouseOut
  39827. *
  39828. * @fires Highcharts.Series#event:mouseOut
  39829. */
  39830. onMouseOut: function () {
  39831. // trigger the event only if listeners exist
  39832. var series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
  39833. // #182, set to null before the mouseOut event fires
  39834. chart.hoverSeries = null;
  39835. // trigger mouse out on the point, which must be in this series
  39836. if (hoverPoint) {
  39837. hoverPoint.onMouseOut();
  39838. }
  39839. // fire the mouse out event
  39840. if (series && options.events.mouseOut) {
  39841. fireEvent(series, 'mouseOut');
  39842. }
  39843. // hide the tooltip
  39844. if (tooltip &&
  39845. !series.stickyTracking &&
  39846. (!tooltip.shared || series.noSharedTooltip)) {
  39847. tooltip.hide();
  39848. }
  39849. // Reset all inactive states
  39850. chart.series.forEach(function (s) {
  39851. s.setState('', true);
  39852. });
  39853. },
  39854. /**
  39855. * Set the state of the series. Called internally on mouse interaction
  39856. * operations, but it can also be called directly to visually
  39857. * highlight a series.
  39858. *
  39859. * @function Highcharts.Series#setState
  39860. *
  39861. * @param {Highcharts.SeriesStateValue|""} [state]
  39862. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  39863. * or `''` (an empty string), `'normal'` or `undefined` to set to
  39864. * normal state.
  39865. * @param {boolean} [inherit]
  39866. * Determines if state should be inherited by points too.
  39867. */
  39868. setState: function (state, inherit) {
  39869. var series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states, lineWidth = options.lineWidth, opacity = options.opacity,
  39870. // By default a quick animation to hover/inactive,
  39871. // slower to un-hover
  39872. stateAnimation = pick((stateOptions[state || 'normal'] &&
  39873. stateOptions[state || 'normal'].animation), series.chart.options.chart.animation), attribs, i = 0;
  39874. state = state || '';
  39875. if (series.state !== state) {
  39876. // Toggle class names
  39877. [
  39878. series.group,
  39879. series.markerGroup,
  39880. series.dataLabelsGroup
  39881. ].forEach(function (group) {
  39882. if (group) {
  39883. // Old state
  39884. if (series.state) {
  39885. group.removeClass('highcharts-series-' + series.state);
  39886. }
  39887. // New state
  39888. if (state) {
  39889. group.addClass('highcharts-series-' + state);
  39890. }
  39891. }
  39892. });
  39893. series.state = state;
  39894. if (!series.chart.styledMode) {
  39895. if (stateOptions[state] &&
  39896. stateOptions[state].enabled === false) {
  39897. return;
  39898. }
  39899. if (state) {
  39900. lineWidth = (stateOptions[state].lineWidth ||
  39901. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  39902. opacity = pick(stateOptions[state].opacity, opacity);
  39903. }
  39904. if (graph && !graph.dashstyle) {
  39905. attribs = {
  39906. 'stroke-width': lineWidth
  39907. };
  39908. // Animate the graph stroke-width.
  39909. graph.animate(attribs, stateAnimation);
  39910. while (series['zone-graph-' + i]) {
  39911. series['zone-graph-' + i].attr(attribs);
  39912. i = i + 1;
  39913. }
  39914. }
  39915. // For some types (pie, networkgraph, sankey) opacity is
  39916. // resolved on a point level
  39917. if (!inactiveOtherPoints) {
  39918. [
  39919. series.group,
  39920. series.markerGroup,
  39921. series.dataLabelsGroup,
  39922. series.labelBySeries
  39923. ].forEach(function (group) {
  39924. if (group) {
  39925. group.animate({
  39926. opacity: opacity
  39927. }, stateAnimation);
  39928. }
  39929. });
  39930. }
  39931. }
  39932. }
  39933. // Don't loop over points on a series that doesn't apply inactive state
  39934. // to siblings markers (e.g. line, column)
  39935. if (inherit && inactiveOtherPoints && series.points) {
  39936. series.setAllPointsToState(state);
  39937. }
  39938. },
  39939. /**
  39940. * Set the state for all points in the series.
  39941. *
  39942. * @function Highcharts.Series#setAllPointsToState
  39943. *
  39944. * @private
  39945. *
  39946. * @param {string} [state]
  39947. * Can be either `hover` or undefined to set to normal state.
  39948. */
  39949. setAllPointsToState: function (state) {
  39950. this.points.forEach(function (point) {
  39951. if (point.setState) {
  39952. point.setState(state);
  39953. }
  39954. });
  39955. },
  39956. /**
  39957. * Show or hide the series.
  39958. *
  39959. * @function Highcharts.Series#setVisible
  39960. *
  39961. * @param {boolean} [visible]
  39962. * True to show the series, false to hide. If undefined, the
  39963. * visibility is toggled.
  39964. *
  39965. * @param {boolean} [redraw=true]
  39966. * Whether to redraw the chart after the series is altered. If doing
  39967. * more operations on the chart, it is a good idea to set redraw to
  39968. * false and call {@link Chart#redraw|chart.redraw()} after.
  39969. *
  39970. * @return {void}
  39971. *
  39972. * @fires Highcharts.Series#event:hide
  39973. * @fires Highcharts.Series#event:show
  39974. */
  39975. setVisible: function (vis, redraw) {
  39976. var series = this, chart = series.chart, legendItem = series.legendItem, showOrHide, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
  39977. // if called without an argument, toggle visibility
  39978. series.visible =
  39979. vis =
  39980. series.options.visible =
  39981. series.userOptions.visible =
  39982. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  39983. showOrHide = vis ? 'show' : 'hide';
  39984. // show or hide elements
  39985. [
  39986. 'group',
  39987. 'dataLabelsGroup',
  39988. 'markerGroup',
  39989. 'tracker',
  39990. 'tt'
  39991. ].forEach(function (key) {
  39992. if (series[key]) {
  39993. series[key][showOrHide]();
  39994. }
  39995. });
  39996. // hide tooltip (#1361)
  39997. if (chart.hoverSeries === series ||
  39998. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  39999. series.onMouseOut();
  40000. }
  40001. if (legendItem) {
  40002. chart.legend.colorizeItem(series, vis);
  40003. }
  40004. // rescale or adapt to resized chart
  40005. series.isDirty = true;
  40006. // in a stack, all other series are affected
  40007. if (series.options.stacking) {
  40008. chart.series.forEach(function (otherSeries) {
  40009. if (otherSeries.options.stacking && otherSeries.visible) {
  40010. otherSeries.isDirty = true;
  40011. }
  40012. });
  40013. }
  40014. // show or hide linked series
  40015. series.linkedSeries.forEach(function (otherSeries) {
  40016. otherSeries.setVisible(vis, false);
  40017. });
  40018. if (ignoreHiddenSeries) {
  40019. chart.isDirtyBox = true;
  40020. }
  40021. fireEvent(series, showOrHide);
  40022. if (redraw !== false) {
  40023. chart.redraw();
  40024. }
  40025. },
  40026. /**
  40027. * Show the series if hidden.
  40028. *
  40029. * @sample highcharts/members/series-hide/
  40030. * Toggle visibility from a button
  40031. *
  40032. * @function Highcharts.Series#show
  40033. * @return {void}
  40034. * @fires Highcharts.Series#event:show
  40035. */
  40036. show: function () {
  40037. this.setVisible(true);
  40038. },
  40039. /**
  40040. * Hide the series if visible. If the {@link
  40041. * https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries|
  40042. * chart.ignoreHiddenSeries} option is true, the chart is redrawn without
  40043. * this series.
  40044. *
  40045. * @sample highcharts/members/series-hide/
  40046. * Toggle visibility from a button
  40047. *
  40048. * @function Highcharts.Series#hide
  40049. * @return {void}
  40050. * @fires Highcharts.Series#event:hide
  40051. */
  40052. hide: function () {
  40053. this.setVisible(false);
  40054. },
  40055. /**
  40056. * Select or unselect the series. This means its
  40057. * {@link Highcharts.Series.selected|selected}
  40058. * property is set, the checkbox in the legend is toggled and when selected,
  40059. * the series is returned by the
  40060. * {@link Highcharts.Chart#getSelectedSeries}
  40061. * function.
  40062. *
  40063. * @sample highcharts/members/series-select/
  40064. * Select a series from a button
  40065. *
  40066. * @function Highcharts.Series#select
  40067. *
  40068. * @param {boolean} [selected]
  40069. * True to select the series, false to unselect. If undefined, the
  40070. * selection state is toggled.
  40071. *
  40072. * @return {void}
  40073. *
  40074. * @fires Highcharts.Series#event:select
  40075. * @fires Highcharts.Series#event:unselect
  40076. */
  40077. select: function (selected) {
  40078. var series = this;
  40079. series.selected =
  40080. selected =
  40081. this.options.selected = (typeof selected === 'undefined' ?
  40082. !series.selected :
  40083. selected);
  40084. if (series.checkbox) {
  40085. series.checkbox.checked = selected;
  40086. }
  40087. fireEvent(series, selected ? 'select' : 'unselect');
  40088. },
  40089. /**
  40090. * @private
  40091. * @borrows Highcharts.TrackerMixin.drawTrackerGraph as Highcharts.Series#drawTracker
  40092. */
  40093. drawTracker: TrackerMixin.drawTrackerGraph
  40094. });
  40095. });
  40096. _registerModule(_modules, 'parts/Responsive.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  40097. /* *
  40098. *
  40099. * (c) 2010-2019 Torstein Honsi
  40100. *
  40101. * License: www.highcharts.com/license
  40102. *
  40103. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40104. *
  40105. * */
  40106. /**
  40107. * A callback function to gain complete control on when the responsive rule
  40108. * applies.
  40109. *
  40110. * @callback Highcharts.ResponsiveCallbackFunction
  40111. *
  40112. * @param {Highcharts.Chart} this
  40113. * Chart context.
  40114. *
  40115. * @return {boolean}
  40116. * Return `true` if it applies.
  40117. */
  40118. var isArray = U.isArray, isObject = U.isObject, objectEach = U.objectEach, pick = U.pick, splat = U.splat;
  40119. var Chart = H.Chart;
  40120. /**
  40121. * Allows setting a set of rules to apply for different screen or chart
  40122. * sizes. Each rule specifies additional chart options.
  40123. *
  40124. * @sample {highstock} stock/demo/responsive/
  40125. * Stock chart
  40126. * @sample highcharts/responsive/axis/
  40127. * Axis
  40128. * @sample highcharts/responsive/legend/
  40129. * Legend
  40130. * @sample highcharts/responsive/classname/
  40131. * Class name
  40132. *
  40133. * @since 5.0.0
  40134. * @apioption responsive
  40135. */
  40136. /**
  40137. * A set of rules for responsive settings. The rules are executed from
  40138. * the top down.
  40139. *
  40140. * @sample {highcharts} highcharts/responsive/axis/
  40141. * Axis changes
  40142. * @sample {highstock} highcharts/responsive/axis/
  40143. * Axis changes
  40144. * @sample {highmaps} highcharts/responsive/axis/
  40145. * Axis changes
  40146. *
  40147. * @type {Array<*>}
  40148. * @since 5.0.0
  40149. * @apioption responsive.rules
  40150. */
  40151. /**
  40152. * A full set of chart options to apply as overrides to the general
  40153. * chart options. The chart options are applied when the given rule
  40154. * is active.
  40155. *
  40156. * A special case is configuration objects that take arrays, for example
  40157. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  40158. * collections, an `id` option is used to map the new option set to
  40159. * an existing object. If an existing object of the same id is not found,
  40160. * the item of the same indexupdated. So for example, setting `chartOptions`
  40161. * with two series items without an `id`, will cause the existing chart's
  40162. * two series to be updated with respective options.
  40163. *
  40164. * @sample {highstock} stock/demo/responsive/
  40165. * Stock chart
  40166. * @sample highcharts/responsive/axis/
  40167. * Axis
  40168. * @sample highcharts/responsive/legend/
  40169. * Legend
  40170. * @sample highcharts/responsive/classname/
  40171. * Class name
  40172. *
  40173. * @type {Highcharts.Options}
  40174. * @since 5.0.0
  40175. * @apioption responsive.rules.chartOptions
  40176. */
  40177. /**
  40178. * Under which conditions the rule applies.
  40179. *
  40180. * @since 5.0.0
  40181. * @apioption responsive.rules.condition
  40182. */
  40183. /**
  40184. * A callback function to gain complete control on when the responsive
  40185. * rule applies. Return `true` if it applies. This opens for checking
  40186. * against other metrics than the chart size, for example the document
  40187. * size or other elements.
  40188. *
  40189. * @type {Highcharts.ResponsiveCallbackFunction}
  40190. * @since 5.0.0
  40191. * @context Highcharts.Chart
  40192. * @apioption responsive.rules.condition.callback
  40193. */
  40194. /**
  40195. * The responsive rule applies if the chart height is less than this.
  40196. *
  40197. * @type {number}
  40198. * @since 5.0.0
  40199. * @apioption responsive.rules.condition.maxHeight
  40200. */
  40201. /**
  40202. * The responsive rule applies if the chart width is less than this.
  40203. *
  40204. * @sample highcharts/responsive/axis/
  40205. * Max width is 500
  40206. *
  40207. * @type {number}
  40208. * @since 5.0.0
  40209. * @apioption responsive.rules.condition.maxWidth
  40210. */
  40211. /**
  40212. * The responsive rule applies if the chart height is greater than this.
  40213. *
  40214. * @type {number}
  40215. * @default 0
  40216. * @since 5.0.0
  40217. * @apioption responsive.rules.condition.minHeight
  40218. */
  40219. /**
  40220. * The responsive rule applies if the chart width is greater than this.
  40221. *
  40222. * @type {number}
  40223. * @default 0
  40224. * @since 5.0.0
  40225. * @apioption responsive.rules.condition.minWidth
  40226. */
  40227. /* eslint-disable no-invalid-this, valid-jsdoc */
  40228. /**
  40229. * Update the chart based on the current chart/document size and options for
  40230. * responsiveness.
  40231. *
  40232. * @private
  40233. * @function Highcharts.Chart#setResponsive
  40234. * @param {boolean} [redraw=true]
  40235. * @param {boolean} [reset=false]
  40236. * Reset by un-applying all rules. Chart.update resets all rules before
  40237. * applying updated options.
  40238. * @return {void}
  40239. */
  40240. Chart.prototype.setResponsive = function (redraw, reset) {
  40241. var options = this.options.responsive, ruleIds = [], currentResponsive = this.currentResponsive, currentRuleIds, undoOptions;
  40242. if (!reset && options && options.rules) {
  40243. options.rules.forEach(function (rule) {
  40244. if (typeof rule._id === 'undefined') {
  40245. rule._id = H.uniqueKey();
  40246. }
  40247. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  40248. }, this);
  40249. }
  40250. // Merge matching rules
  40251. var mergedOptions = H.merge.apply(0, ruleIds.map(function (ruleId) {
  40252. return H.find(options.rules, function (rule) {
  40253. return rule._id === ruleId;
  40254. }).chartOptions;
  40255. }));
  40256. mergedOptions.isResponsiveOptions = true;
  40257. // Stringified key for the rules that currently apply.
  40258. ruleIds = (ruleIds.toString() || void 0);
  40259. currentRuleIds = currentResponsive && currentResponsive.ruleIds;
  40260. // Changes in what rules apply
  40261. if (ruleIds !== currentRuleIds) {
  40262. // Undo previous rules. Before we apply a new set of rules, we need to
  40263. // roll back completely to base options (#6291).
  40264. if (currentResponsive) {
  40265. this.update(currentResponsive.undoOptions, redraw, true);
  40266. }
  40267. if (ruleIds) {
  40268. // Get undo-options for matching rules
  40269. undoOptions = this.currentOptions(mergedOptions);
  40270. undoOptions.isResponsiveOptions = true;
  40271. this.currentResponsive = {
  40272. ruleIds: ruleIds,
  40273. mergedOptions: mergedOptions,
  40274. undoOptions: undoOptions
  40275. };
  40276. this.update(mergedOptions, redraw, true);
  40277. }
  40278. else {
  40279. this.currentResponsive = void 0;
  40280. }
  40281. }
  40282. };
  40283. /**
  40284. * Handle a single responsiveness rule.
  40285. *
  40286. * @private
  40287. * @function Highcharts.Chart#matchResponsiveRule
  40288. * @param {Highcharts.ResponsiveRulesOptions} rule
  40289. * @param {Array<string>} matches
  40290. * @return {void}
  40291. */
  40292. Chart.prototype.matchResponsiveRule = function (rule, matches) {
  40293. var condition = rule.condition, fn = condition.callback || function () {
  40294. return (this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  40295. this.chartHeight <=
  40296. pick(condition.maxHeight, Number.MAX_VALUE) &&
  40297. this.chartWidth >= pick(condition.minWidth, 0) &&
  40298. this.chartHeight >= pick(condition.minHeight, 0));
  40299. };
  40300. if (fn.call(this)) {
  40301. matches.push(rule._id);
  40302. }
  40303. };
  40304. /**
  40305. * Get the current values for a given set of options. Used before we update
  40306. * the chart with a new responsiveness rule.
  40307. * TODO: Restore axis options (by id?)
  40308. *
  40309. * @private
  40310. * @function Highcharts.Chart#currentOptions
  40311. * @param {Highcharts.Options} options
  40312. * @return {Highcharts.Options}
  40313. */
  40314. Chart.prototype.currentOptions = function (options) {
  40315. var chart = this, ret = {};
  40316. /**
  40317. * Recurse over a set of options and its current values,
  40318. * and store the current values in the ret object.
  40319. */
  40320. function getCurrent(options, curr, ret, depth) {
  40321. var i;
  40322. objectEach(options, function (val, key) {
  40323. if (!depth &&
  40324. chart.collectionsWithUpdate.indexOf(key) > -1) {
  40325. val = splat(val);
  40326. ret[key] = [];
  40327. // Iterate over collections like series, xAxis or yAxis and map
  40328. // the items by index.
  40329. for (i = 0; i < val.length; i++) {
  40330. if (curr[key][i]) { // Item exists in current data (#6347)
  40331. ret[key][i] = {};
  40332. getCurrent(val[i], curr[key][i], ret[key][i], depth + 1);
  40333. }
  40334. }
  40335. }
  40336. else if (isObject(val)) {
  40337. ret[key] = isArray(val) ? [] : {};
  40338. getCurrent(val, curr[key] || {}, ret[key], depth + 1);
  40339. }
  40340. else if (typeof curr[key] === 'undefined') { // #10286
  40341. ret[key] = null;
  40342. }
  40343. else {
  40344. ret[key] = curr[key];
  40345. }
  40346. });
  40347. }
  40348. getCurrent(options, this.options, ret, 0);
  40349. return ret;
  40350. };
  40351. });
  40352. _registerModule(_modules, 'masters/highcharts.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  40353. var extend = U.extend;
  40354. extend(Highcharts, {
  40355. animObject: U.animObject,
  40356. arrayMax: U.arrayMax,
  40357. arrayMin: U.arrayMin,
  40358. attr: U.attr,
  40359. correctFloat: U.correctFloat,
  40360. defined: U.defined,
  40361. destroyObjectProperties: U.destroyObjectProperties,
  40362. discardElement: U.discardElement,
  40363. erase: U.erase,
  40364. extend: U.extend,
  40365. extendClass: U.extendClass,
  40366. isArray: U.isArray,
  40367. isClass: U.isClass,
  40368. isDOMElement: U.isDOMElement,
  40369. isNumber: U.isNumber,
  40370. isObject: U.isObject,
  40371. isString: U.isString,
  40372. numberFormat: U.numberFormat,
  40373. objectEach: U.objectEach,
  40374. offset: U.offset,
  40375. pad: U.pad,
  40376. pick: U.pick,
  40377. pInt: U.pInt,
  40378. relativeLength: U.relativeLength,
  40379. setAnimation: U.setAnimation,
  40380. splat: U.splat,
  40381. syncTimeout: U.syncTimeout,
  40382. wrap: U.wrap
  40383. });
  40384. return Highcharts;
  40385. });
  40386. _registerModule(_modules, 'parts/Scrollbar.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  40387. /* *
  40388. *
  40389. * (c) 2010-2019 Torstein Honsi
  40390. *
  40391. * License: www.highcharts.com/license
  40392. *
  40393. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40394. *
  40395. * */
  40396. var correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, pick = U.pick;
  40397. var addEvent = H.addEvent, Axis = H.Axis, defaultOptions = H.defaultOptions, fireEvent = H.fireEvent, hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice, merge = H.merge, removeEvent = H.removeEvent, swapXY;
  40398. /**
  40399. *
  40400. * The scrollbar is a means of panning over the X axis of a stock chart.
  40401. * Scrollbars can also be applied to other types of axes.
  40402. *
  40403. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  40404. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  40405. * is especially suitable for simpler cartesian charts on mobile.
  40406. *
  40407. * In styled mode, all the presentational options for the
  40408. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  40409. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  40410. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  40411. *
  40412. * @sample stock/yaxis/inverted-bar-scrollbar/
  40413. * A scrollbar on a simple bar chart
  40414. *
  40415. * @product highstock gantt
  40416. * @optionparent scrollbar
  40417. */
  40418. var defaultScrollbarOptions = {
  40419. /**
  40420. * The height of the scrollbar. The height also applies to the width
  40421. * of the scroll arrows so that they are always squares. Defaults to
  40422. * 20 for touch devices and 14 for mouse devices.
  40423. *
  40424. * @sample stock/scrollbar/height/
  40425. * A 30px scrollbar
  40426. *
  40427. * @type {number}
  40428. * @default 20/14
  40429. */
  40430. height: isTouchDevice ? 20 : 14,
  40431. /**
  40432. * The border rounding radius of the bar.
  40433. *
  40434. * @sample stock/scrollbar/style/
  40435. * Scrollbar styling
  40436. */
  40437. barBorderRadius: 0,
  40438. /**
  40439. * The corner radius of the scrollbar buttons.
  40440. *
  40441. * @sample stock/scrollbar/style/
  40442. * Scrollbar styling
  40443. */
  40444. buttonBorderRadius: 0,
  40445. /**
  40446. * Enable or disable the scrollbar.
  40447. *
  40448. * @sample stock/scrollbar/enabled/
  40449. * Disable the scrollbar, only use navigator
  40450. *
  40451. * @type {boolean}
  40452. * @default true
  40453. * @apioption scrollbar.enabled
  40454. */
  40455. /**
  40456. * Whether to redraw the main chart as the scrollbar or the navigator
  40457. * zoomed window is moved. Defaults to `true` for modern browsers and
  40458. * `false` for legacy IE browsers as well as mobile devices.
  40459. *
  40460. * @sample stock/scrollbar/liveredraw
  40461. * Setting live redraw to false
  40462. *
  40463. * @type {boolean}
  40464. * @since 1.3
  40465. */
  40466. liveRedraw: void 0,
  40467. /**
  40468. * The margin between the scrollbar and its axis when the scrollbar is
  40469. * applied directly to an axis.
  40470. */
  40471. margin: 10,
  40472. /**
  40473. * The minimum width of the scrollbar.
  40474. *
  40475. * @since 1.2.5
  40476. */
  40477. minWidth: 6,
  40478. /**
  40479. * Whether to show or hide the scrollbar when the scrolled content is
  40480. * zoomed out to it full extent.
  40481. *
  40482. * @type {boolean}
  40483. * @default true
  40484. * @apioption scrollbar.showFull
  40485. */
  40486. step: 0.2,
  40487. /**
  40488. * The z index of the scrollbar group.
  40489. */
  40490. zIndex: 3,
  40491. /**
  40492. * The background color of the scrollbar itself.
  40493. *
  40494. * @sample stock/scrollbar/style/
  40495. * Scrollbar styling
  40496. *
  40497. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40498. */
  40499. barBackgroundColor: '#cccccc',
  40500. /**
  40501. * The width of the bar's border.
  40502. *
  40503. * @sample stock/scrollbar/style/
  40504. * Scrollbar styling
  40505. */
  40506. barBorderWidth: 1,
  40507. /**
  40508. * The color of the scrollbar's border.
  40509. *
  40510. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40511. */
  40512. barBorderColor: '#cccccc',
  40513. /**
  40514. * The color of the small arrow inside the scrollbar buttons.
  40515. *
  40516. * @sample stock/scrollbar/style/
  40517. * Scrollbar styling
  40518. *
  40519. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40520. */
  40521. buttonArrowColor: '#333333',
  40522. /**
  40523. * The color of scrollbar buttons.
  40524. *
  40525. * @sample stock/scrollbar/style/
  40526. * Scrollbar styling
  40527. *
  40528. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40529. */
  40530. buttonBackgroundColor: '#e6e6e6',
  40531. /**
  40532. * The color of the border of the scrollbar buttons.
  40533. *
  40534. * @sample stock/scrollbar/style/
  40535. * Scrollbar styling
  40536. *
  40537. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40538. */
  40539. buttonBorderColor: '#cccccc',
  40540. /**
  40541. * The border width of the scrollbar buttons.
  40542. *
  40543. * @sample stock/scrollbar/style/
  40544. * Scrollbar styling
  40545. */
  40546. buttonBorderWidth: 1,
  40547. /**
  40548. * The color of the small rifles in the middle of the scrollbar.
  40549. *
  40550. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40551. */
  40552. rifleColor: '#333333',
  40553. /**
  40554. * The color of the track background.
  40555. *
  40556. * @sample stock/scrollbar/style/
  40557. * Scrollbar styling
  40558. *
  40559. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40560. */
  40561. trackBackgroundColor: '#f2f2f2',
  40562. /**
  40563. * The color of the border of the scrollbar track.
  40564. *
  40565. * @sample stock/scrollbar/style/
  40566. * Scrollbar styling
  40567. *
  40568. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40569. */
  40570. trackBorderColor: '#f2f2f2',
  40571. /**
  40572. * The corner radius of the border of the scrollbar track.
  40573. *
  40574. * @sample stock/scrollbar/style/
  40575. * Scrollbar styling
  40576. *
  40577. * @type {number}
  40578. * @default 0
  40579. * @apioption scrollbar.trackBorderRadius
  40580. */
  40581. /**
  40582. * The width of the border of the scrollbar track.
  40583. *
  40584. * @sample stock/scrollbar/style/
  40585. * Scrollbar styling
  40586. */
  40587. trackBorderWidth: 1
  40588. };
  40589. defaultOptions.scrollbar = merge(true, defaultScrollbarOptions, defaultOptions.scrollbar);
  40590. /**
  40591. * When we have vertical scrollbar, rifles and arrow in buttons should be
  40592. * rotated. The same method is used in Navigator's handles, to rotate them.
  40593. *
  40594. * @function Highcharts.swapXY
  40595. *
  40596. * @param {Highcharts.SVGPathArray} path
  40597. * Path to be rotated.
  40598. *
  40599. * @param {boolean} [vertical]
  40600. * If vertical scrollbar, swap x-y values.
  40601. *
  40602. * @return {Highcharts.SVGPathArray}
  40603. * Rotated path.
  40604. */
  40605. H.swapXY = swapXY = function (path, vertical) {
  40606. var i, len = path.length, temp;
  40607. if (vertical) {
  40608. for (i = 0; i < len; i += 3) {
  40609. temp = path[i + 1];
  40610. path[i + 1] = path[i + 2];
  40611. path[i + 2] = temp;
  40612. }
  40613. }
  40614. return path;
  40615. };
  40616. /* eslint-disable no-invalid-this, valid-jsdoc */
  40617. /**
  40618. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  40619. * on individual axes.
  40620. *
  40621. * @private
  40622. * @class
  40623. * @name Highcharts.Scrollbar
  40624. * @param {Highcharts.SVGRenderer} renderer
  40625. * @param {Highcharts.ScrollbarOptions} options
  40626. * @param {Highcharts.Chart} chart
  40627. */
  40628. function Scrollbar(renderer, options, chart) {
  40629. this.init(renderer, options, chart);
  40630. }
  40631. Scrollbar.prototype = {
  40632. /**
  40633. * @private
  40634. * @function Highcharts.Scrollbar#init
  40635. * @param {Highcharts.SVGRenderer} renderer
  40636. * @param {Highcharts.ScrollbarOptions} options
  40637. * @param {Highcharts.Chart} chart
  40638. * @return {void}
  40639. */
  40640. init: function (renderer, options, chart) {
  40641. this.scrollbarButtons = [];
  40642. this.renderer = renderer;
  40643. this.userOptions = options;
  40644. this.options = merge(defaultScrollbarOptions, options);
  40645. this.chart = chart;
  40646. // backward compatibility
  40647. this.size = pick(this.options.size, this.options.height);
  40648. // Init
  40649. if (options.enabled) {
  40650. this.render();
  40651. this.initEvents();
  40652. this.addEvents();
  40653. }
  40654. },
  40655. /**
  40656. * Render scrollbar with all required items.
  40657. *
  40658. * @private
  40659. * @function Highcharts.Scrollbar#render
  40660. */
  40661. render: function () {
  40662. var scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = this.chart.styledMode, group;
  40663. // Draw the scrollbar group
  40664. scroller.group = group = renderer.g('scrollbar').attr({
  40665. zIndex: options.zIndex,
  40666. translateY: -99999
  40667. }).add();
  40668. // Draw the scrollbar track:
  40669. scroller.track = renderer.rect()
  40670. .addClass('highcharts-scrollbar-track')
  40671. .attr({
  40672. x: 0,
  40673. r: options.trackBorderRadius || 0,
  40674. height: size,
  40675. width: size
  40676. }).add(group);
  40677. if (!styledMode) {
  40678. scroller.track.attr({
  40679. fill: options.trackBackgroundColor,
  40680. stroke: options.trackBorderColor,
  40681. 'stroke-width': options.trackBorderWidth
  40682. });
  40683. }
  40684. this.trackBorderWidth = scroller.track.strokeWidth();
  40685. scroller.track.attr({
  40686. y: -this.trackBorderWidth % 2 / 2
  40687. });
  40688. // Draw the scrollbar itself
  40689. scroller.scrollbarGroup = renderer.g().add(group);
  40690. scroller.scrollbar = renderer.rect()
  40691. .addClass('highcharts-scrollbar-thumb')
  40692. .attr({
  40693. height: size,
  40694. width: size,
  40695. r: options.barBorderRadius || 0
  40696. }).add(scroller.scrollbarGroup);
  40697. scroller.scrollbarRifles = renderer
  40698. .path(swapXY([
  40699. 'M',
  40700. -3, size / 4,
  40701. 'L',
  40702. -3, 2 * size / 3,
  40703. 'M',
  40704. 0, size / 4,
  40705. 'L',
  40706. 0, 2 * size / 3,
  40707. 'M',
  40708. 3, size / 4,
  40709. 'L',
  40710. 3, 2 * size / 3
  40711. ], options.vertical))
  40712. .addClass('highcharts-scrollbar-rifles')
  40713. .add(scroller.scrollbarGroup);
  40714. if (!styledMode) {
  40715. scroller.scrollbar.attr({
  40716. fill: options.barBackgroundColor,
  40717. stroke: options.barBorderColor,
  40718. 'stroke-width': options.barBorderWidth
  40719. });
  40720. scroller.scrollbarRifles.attr({
  40721. stroke: options.rifleColor,
  40722. 'stroke-width': 1
  40723. });
  40724. }
  40725. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  40726. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  40727. // Draw the buttons:
  40728. scroller.drawScrollbarButton(0);
  40729. scroller.drawScrollbarButton(1);
  40730. },
  40731. /**
  40732. * Position the scrollbar, method called from a parent with defined
  40733. * dimensions.
  40734. *
  40735. * @private
  40736. * @function Highcharts.Scrollbar#position
  40737. * @param {number} x
  40738. * x-position on the chart
  40739. * @param {number} y
  40740. * y-position on the chart
  40741. * @param {number} width
  40742. * width of the scrollbar
  40743. * @param {number} height
  40744. * height of the scorllbar
  40745. * @return {void}
  40746. */
  40747. position: function (x, y, width, height) {
  40748. var scroller = this, options = scroller.options, vertical = options.vertical, xOffset = height, yOffset = 0, method = scroller.rendered ? 'animate' : 'attr';
  40749. scroller.x = x;
  40750. scroller.y = y + this.trackBorderWidth;
  40751. scroller.width = width; // width with buttons
  40752. scroller.height = height;
  40753. scroller.xOffset = xOffset;
  40754. scroller.yOffset = yOffset;
  40755. // If Scrollbar is a vertical type, swap options:
  40756. if (vertical) {
  40757. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  40758. scroller.xOffset = xOffset = 0;
  40759. scroller.barWidth = height - width * 2; // width without buttons
  40760. scroller.x = x = x + scroller.options.margin;
  40761. }
  40762. else {
  40763. scroller.height = scroller.xOffset = height = xOffset =
  40764. scroller.size;
  40765. scroller.barWidth = width - height * 2; // width without buttons
  40766. scroller.y = scroller.y + scroller.options.margin;
  40767. }
  40768. // Set general position for a group:
  40769. scroller.group[method]({
  40770. translateX: x,
  40771. translateY: scroller.y
  40772. });
  40773. // Resize background/track:
  40774. scroller.track[method]({
  40775. width: width,
  40776. height: height
  40777. });
  40778. // Move right/bottom button ot it's place:
  40779. scroller.scrollbarButtons[1][method]({
  40780. translateX: vertical ? 0 : width - xOffset,
  40781. translateY: vertical ? height - yOffset : 0
  40782. });
  40783. },
  40784. /**
  40785. * Draw the scrollbar buttons with arrows
  40786. *
  40787. * @private
  40788. * @function Highcharts.Scrollbar#drawScrollbarButton
  40789. * @param {number} index
  40790. * 0 is left, 1 is right
  40791. * @return {void}
  40792. */
  40793. drawScrollbarButton: function (index) {
  40794. var scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group, tempElem;
  40795. group = renderer.g().add(scroller.group);
  40796. scrollbarButtons.push(group);
  40797. // Create a rectangle for the scrollbar button
  40798. tempElem = renderer.rect()
  40799. .addClass('highcharts-scrollbar-button')
  40800. .add(group);
  40801. // Presentational attributes
  40802. if (!this.chart.styledMode) {
  40803. tempElem.attr({
  40804. stroke: options.buttonBorderColor,
  40805. 'stroke-width': options.buttonBorderWidth,
  40806. fill: options.buttonBackgroundColor
  40807. });
  40808. }
  40809. // Place the rectangle based on the rendered stroke width
  40810. tempElem.attr(tempElem.crisp({
  40811. x: -0.5,
  40812. y: -0.5,
  40813. width: size + 1,
  40814. height: size + 1,
  40815. r: options.buttonBorderRadius
  40816. }, tempElem.strokeWidth()));
  40817. // Button arrow
  40818. tempElem = renderer
  40819. .path(swapXY([
  40820. 'M',
  40821. size / 2 + (index ? -1 : 1),
  40822. size / 2 - 3,
  40823. 'L',
  40824. size / 2 + (index ? -1 : 1),
  40825. size / 2 + 3,
  40826. 'L',
  40827. size / 2 + (index ? 2 : -2),
  40828. size / 2
  40829. ], options.vertical))
  40830. .addClass('highcharts-scrollbar-arrow')
  40831. .add(scrollbarButtons[index]);
  40832. if (!this.chart.styledMode) {
  40833. tempElem.attr({
  40834. fill: options.buttonArrowColor
  40835. });
  40836. }
  40837. },
  40838. /**
  40839. * Set scrollbar size, with a given scale.
  40840. *
  40841. * @private
  40842. * @function Highcharts.Scrollbar#setRange
  40843. * @param {number} from
  40844. * scale (0-1) where bar should start
  40845. * @param {number} to
  40846. * scale (0-1) where bar should end
  40847. * @return {void}
  40848. */
  40849. setRange: function (from, to) {
  40850. var scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, fromPX, toPX, newPos, newSize, newRiflesPos, method = (this.rendered &&
  40851. !this.hasDragged &&
  40852. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  40853. if (!defined(fullWidth)) {
  40854. return;
  40855. }
  40856. from = Math.max(from, 0);
  40857. fromPX = Math.ceil(fullWidth * from);
  40858. toPX = fullWidth * Math.min(to, 1);
  40859. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  40860. // We need to recalculate position, if minWidth is used
  40861. if (newSize < minWidth) {
  40862. fromPX = (fullWidth - minWidth + newSize) * from;
  40863. newSize = minWidth;
  40864. }
  40865. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  40866. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  40867. // Store current position:
  40868. scroller.from = from;
  40869. scroller.to = to;
  40870. if (!vertical) {
  40871. scroller.scrollbarGroup[method]({
  40872. translateX: newPos
  40873. });
  40874. scroller.scrollbar[method]({
  40875. width: newSize
  40876. });
  40877. scroller.scrollbarRifles[method]({
  40878. translateX: newRiflesPos
  40879. });
  40880. scroller.scrollbarLeft = newPos;
  40881. scroller.scrollbarTop = 0;
  40882. }
  40883. else {
  40884. scroller.scrollbarGroup[method]({
  40885. translateY: newPos
  40886. });
  40887. scroller.scrollbar[method]({
  40888. height: newSize
  40889. });
  40890. scroller.scrollbarRifles[method]({
  40891. translateY: newRiflesPos
  40892. });
  40893. scroller.scrollbarTop = newPos;
  40894. scroller.scrollbarLeft = 0;
  40895. }
  40896. if (newSize <= 12) {
  40897. scroller.scrollbarRifles.hide();
  40898. }
  40899. else {
  40900. scroller.scrollbarRifles.show(true);
  40901. }
  40902. // Show or hide the scrollbar based on the showFull setting
  40903. if (options.showFull === false) {
  40904. if (from <= 0 && to >= 1) {
  40905. scroller.group.hide();
  40906. }
  40907. else {
  40908. scroller.group.show();
  40909. }
  40910. }
  40911. scroller.rendered = true;
  40912. },
  40913. /**
  40914. * Init events methods, so we have an access to the Scrollbar itself
  40915. *
  40916. * @private
  40917. * @function Highcharts.Scrollbar#initEvents
  40918. * @return {void}
  40919. * @fires Highcharts.Scrollbar#event:changed
  40920. */
  40921. initEvents: function () {
  40922. var scroller = this;
  40923. /**
  40924. * Event handler for the mouse move event.
  40925. * @private
  40926. */
  40927. scroller.mouseMoveHandler = function (e) {
  40928. var normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ? 'chartY' : 'chartX', initPositions = scroller.initPositions, scrollPosition, chartPosition, change;
  40929. // In iOS, a mousemove event with e.pageX === 0 is fired when
  40930. // holding the finger down in the center of the scrollbar. This
  40931. // should be ignored.
  40932. if (scroller.grabbedCenter &&
  40933. // #4696, scrollbar failed on Android
  40934. (!e.touches || e.touches[0][direction] !== 0)) {
  40935. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  40936. scrollPosition = scroller[direction];
  40937. change = chartPosition - scrollPosition;
  40938. scroller.hasDragged = true;
  40939. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  40940. if (scroller.hasDragged) {
  40941. fireEvent(scroller, 'changed', {
  40942. from: scroller.from,
  40943. to: scroller.to,
  40944. trigger: 'scrollbar',
  40945. DOMType: e.type,
  40946. DOMEvent: e
  40947. });
  40948. }
  40949. }
  40950. };
  40951. /**
  40952. * Event handler for the mouse up event.
  40953. * @private
  40954. */
  40955. scroller.mouseUpHandler = function (e) {
  40956. if (scroller.hasDragged) {
  40957. fireEvent(scroller, 'changed', {
  40958. from: scroller.from,
  40959. to: scroller.to,
  40960. trigger: 'scrollbar',
  40961. DOMType: e.type,
  40962. DOMEvent: e
  40963. });
  40964. }
  40965. scroller.grabbedCenter =
  40966. scroller.hasDragged =
  40967. scroller.chartX =
  40968. scroller.chartY = null;
  40969. };
  40970. scroller.mouseDownHandler = function (e) {
  40971. var normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  40972. scroller.chartX = mousePosition.chartX;
  40973. scroller.chartY = mousePosition.chartY;
  40974. scroller.initPositions = [scroller.from, scroller.to];
  40975. scroller.grabbedCenter = true;
  40976. };
  40977. scroller.buttonToMinClick = function (e) {
  40978. var range = correctFloat(scroller.to - scroller.from) *
  40979. scroller.options.step;
  40980. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  40981. fireEvent(scroller, 'changed', {
  40982. from: scroller.from,
  40983. to: scroller.to,
  40984. trigger: 'scrollbar',
  40985. DOMEvent: e
  40986. });
  40987. };
  40988. scroller.buttonToMaxClick = function (e) {
  40989. var range = (scroller.to - scroller.from) * scroller.options.step;
  40990. scroller.updatePosition(scroller.from + range, scroller.to + range);
  40991. fireEvent(scroller, 'changed', {
  40992. from: scroller.from,
  40993. to: scroller.to,
  40994. trigger: 'scrollbar',
  40995. DOMEvent: e
  40996. });
  40997. };
  40998. scroller.trackClick = function (e) {
  40999. var normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;
  41000. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  41001. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  41002. // On the top or on the left side of the track:
  41003. scroller.updatePosition(scroller.from + range, scroller.to + range);
  41004. }
  41005. else {
  41006. // On the bottom or the right side of the track:
  41007. scroller.updatePosition(scroller.from - range, scroller.to - range);
  41008. }
  41009. fireEvent(scroller, 'changed', {
  41010. from: scroller.from,
  41011. to: scroller.to,
  41012. trigger: 'scrollbar',
  41013. DOMEvent: e
  41014. });
  41015. };
  41016. },
  41017. /**
  41018. * Get normalized (0-1) cursor position over the scrollbar
  41019. *
  41020. * @private
  41021. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  41022. *
  41023. * @param {*} normalizedEvent
  41024. * normalized event, with chartX and chartY values
  41025. *
  41026. * @return {Highcharts.Dictionary<number>}
  41027. * Local position {chartX, chartY}
  41028. */
  41029. cursorToScrollbarPosition: function (normalizedEvent) {
  41030. var scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  41031. options.minWidth :
  41032. 0; // minWidth distorts translation
  41033. return {
  41034. chartX: (normalizedEvent.chartX - scroller.x -
  41035. scroller.xOffset) /
  41036. (scroller.barWidth - minWidthDifference),
  41037. chartY: (normalizedEvent.chartY - scroller.y -
  41038. scroller.yOffset) /
  41039. (scroller.barWidth - minWidthDifference)
  41040. };
  41041. },
  41042. /**
  41043. * Update position option in the Scrollbar, with normalized 0-1 scale
  41044. *
  41045. * @private
  41046. * @function Highcharts.Scrollbar#updatePosition
  41047. * @param {number} from
  41048. * @param {number} to
  41049. * @return {void}
  41050. */
  41051. updatePosition: function (from, to) {
  41052. if (to > 1) {
  41053. from = correctFloat(1 - correctFloat(to - from));
  41054. to = 1;
  41055. }
  41056. if (from < 0) {
  41057. to = correctFloat(to - from);
  41058. from = 0;
  41059. }
  41060. this.from = from;
  41061. this.to = to;
  41062. },
  41063. /**
  41064. * Update the scrollbar with new options
  41065. *
  41066. * @private
  41067. * @function Highcharts.Scrollbar#update
  41068. * @param {Highcharts.ScrollbarOptions} options
  41069. * @return {void}
  41070. */
  41071. update: function (options) {
  41072. this.destroy();
  41073. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  41074. },
  41075. /**
  41076. * Set up the mouse and touch events for the Scrollbar
  41077. *
  41078. * @private
  41079. * @function Highcharts.Scrollbar#addEvents
  41080. * @return {void}
  41081. */
  41082. addEvents: function () {
  41083. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1], buttons = this.scrollbarButtons, bar = this.scrollbarGroup.element, track = this.track.element, mouseDownHandler = this.mouseDownHandler, mouseMoveHandler = this.mouseMoveHandler, mouseUpHandler = this.mouseUpHandler, _events;
  41084. // Mouse events
  41085. _events = [
  41086. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick],
  41087. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick],
  41088. [track, 'click', this.trackClick],
  41089. [bar, 'mousedown', mouseDownHandler],
  41090. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  41091. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  41092. ];
  41093. // Touch events
  41094. if (hasTouch) {
  41095. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  41096. }
  41097. // Add them all
  41098. _events.forEach(function (args) {
  41099. addEvent.apply(null, args);
  41100. });
  41101. this._events = _events;
  41102. },
  41103. /**
  41104. * Removes the event handlers attached previously with addEvents.
  41105. *
  41106. * @private
  41107. * @function Highcharts.Scrollbar#removeEvents
  41108. * @return {void}
  41109. */
  41110. removeEvents: function () {
  41111. this._events.forEach(function (args) {
  41112. removeEvent.apply(null, args);
  41113. });
  41114. this._events.length = 0;
  41115. },
  41116. /**
  41117. * Destroys allocated elements.
  41118. *
  41119. * @private
  41120. * @function Highcharts.Scrollbar#destroy
  41121. * @return {void}
  41122. */
  41123. destroy: function () {
  41124. var scroller = this.chart.scroller;
  41125. // Disconnect events added in addEvents
  41126. this.removeEvents();
  41127. // Destroy properties
  41128. [
  41129. 'track',
  41130. 'scrollbarRifles',
  41131. 'scrollbar',
  41132. 'scrollbarGroup',
  41133. 'group'
  41134. ].forEach(function (prop) {
  41135. if (this[prop] && this[prop].destroy) {
  41136. this[prop] = this[prop].destroy();
  41137. }
  41138. }, this);
  41139. // #6421, chart may have more scrollbars
  41140. if (scroller && this === scroller.scrollbar) {
  41141. scroller.scrollbar = null;
  41142. // Destroy elements in collection
  41143. destroyObjectProperties(scroller.scrollbarButtons);
  41144. }
  41145. }
  41146. };
  41147. if (!H.Scrollbar) {
  41148. /* *
  41149. * Wrap axis initialization and create scrollbar if enabled:
  41150. */
  41151. addEvent(Axis, 'afterInit', function () {
  41152. var axis = this;
  41153. if (axis.options &&
  41154. axis.options.scrollbar &&
  41155. axis.options.scrollbar.enabled) {
  41156. // Predefined options:
  41157. axis.options.scrollbar.vertical = !axis.horiz;
  41158. axis.options.startOnTick = axis.options.endOnTick = false;
  41159. axis.scrollbar = new Scrollbar(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  41160. addEvent(axis.scrollbar, 'changed', function (e) {
  41161. var unitedMin = Math.min(pick(axis.options.min, axis.min), axis.min, axis.dataMin), unitedMax = Math.max(pick(axis.options.max, axis.max), axis.max, axis.dataMax), range = unitedMax - unitedMin, to, from;
  41162. if ((axis.horiz && !axis.reversed) ||
  41163. (!axis.horiz && axis.reversed)) {
  41164. to = unitedMin + range * this.to;
  41165. from = unitedMin + range * this.from;
  41166. }
  41167. else {
  41168. // y-values in browser are reversed, but this also applies
  41169. // for reversed horizontal axis:
  41170. to = unitedMin + range * (1 - this.from);
  41171. from = unitedMin + range * (1 - this.to);
  41172. }
  41173. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  41174. // Mouseup always should change extremes
  41175. e.DOMType === 'mouseup' ||
  41176. // Internal events
  41177. !defined(e.DOMType)) {
  41178. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove', e);
  41179. }
  41180. else {
  41181. // When live redraw is disabled, don't change extremes
  41182. // Only change the position of the scollbar thumb
  41183. this.setRange(this.from, this.to);
  41184. }
  41185. });
  41186. }
  41187. });
  41188. /* *
  41189. * Wrap rendering axis, and update scrollbar if one is created:
  41190. */
  41191. addEvent(Axis, 'afterRender', function () {
  41192. var axis = this, scrollMin = Math.min(pick(axis.options.min, axis.min), axis.min, pick(axis.dataMin, axis.min) // #6930
  41193. ), scrollMax = Math.max(pick(axis.options.max, axis.max), axis.max, pick(axis.dataMax, axis.max) // #6930
  41194. ), scrollbar = axis.scrollbar, offset = axis.axisTitleMargin + (axis.titleOffset || 0), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0, offsetsIndex, from, to;
  41195. if (scrollbar) {
  41196. if (axis.horiz) {
  41197. // Reserve space for labels/title
  41198. if (!axis.opposite) {
  41199. scrollbarsOffsets[1] += offset;
  41200. }
  41201. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  41202. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  41203. // Next scrollbar should reserve space for margin (if set)
  41204. if (!axis.opposite) {
  41205. scrollbarsOffsets[1] += axisMargin;
  41206. }
  41207. offsetsIndex = 1;
  41208. }
  41209. else {
  41210. // Reserve space for labels/title
  41211. if (axis.opposite) {
  41212. scrollbarsOffsets[0] += offset;
  41213. }
  41214. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  41215. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  41216. // Next scrollbar should reserve space for margin (if set)
  41217. if (axis.opposite) {
  41218. scrollbarsOffsets[0] += axisMargin;
  41219. }
  41220. offsetsIndex = 0;
  41221. }
  41222. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  41223. scrollbar.options.margin;
  41224. if (isNaN(scrollMin) ||
  41225. isNaN(scrollMax) ||
  41226. !defined(axis.min) ||
  41227. !defined(axis.max) ||
  41228. axis.min === axis.max // #10733
  41229. ) {
  41230. // default action: when extremes are the same or there is not
  41231. // extremes on the axis, but scrollbar exists, make it full size
  41232. scrollbar.setRange(0, 1);
  41233. }
  41234. else {
  41235. from =
  41236. (axis.min - scrollMin) / (scrollMax - scrollMin);
  41237. to =
  41238. (axis.max - scrollMin) / (scrollMax - scrollMin);
  41239. if ((axis.horiz && !axis.reversed) ||
  41240. (!axis.horiz && axis.reversed)) {
  41241. scrollbar.setRange(from, to);
  41242. }
  41243. else {
  41244. // inverse vertical axis
  41245. scrollbar.setRange(1 - to, 1 - from);
  41246. }
  41247. }
  41248. }
  41249. });
  41250. /* *
  41251. * Make space for a scrollbar
  41252. * @private
  41253. */
  41254. addEvent(Axis, 'afterGetOffset', function () {
  41255. var axis = this, index = axis.horiz ? 2 : 1, scrollbar = axis.scrollbar;
  41256. if (scrollbar) {
  41257. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  41258. axis.chart.axisOffset[index] +=
  41259. scrollbar.size + scrollbar.options.margin;
  41260. }
  41261. });
  41262. H.Scrollbar = Scrollbar;
  41263. }
  41264. });
  41265. _registerModule(_modules, 'parts/Navigator.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  41266. /* *
  41267. *
  41268. * (c) 2010-2019 Torstein Honsi
  41269. *
  41270. * License: www.highcharts.com/license
  41271. *
  41272. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41273. *
  41274. * */
  41275. var clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick, splat = U.splat;
  41276. var addEvent = H.addEvent, Axis = H.Axis, Chart = H.Chart, color = H.color, defaultOptions = H.defaultOptions, hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice, merge = H.merge, removeEvent = H.removeEvent, Scrollbar = H.Scrollbar, Series = H.Series, seriesTypes = H.seriesTypes, defaultSeriesType,
  41277. // Finding the min or max of a set of variables where we don't know if they
  41278. // are defined, is a pattern that is repeated several places in Highcharts.
  41279. // Consider making this a global utility method.
  41280. numExt = function (extreme) {
  41281. var args = [];
  41282. for (var _i = 1; _i < arguments.length; _i++) {
  41283. args[_i - 1] = arguments[_i];
  41284. }
  41285. var numbers = [].filter.call(args, isNumber);
  41286. if (numbers.length) {
  41287. return Math[extreme].apply(0, numbers);
  41288. }
  41289. };
  41290. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  41291. 'line' :
  41292. 'areaspline';
  41293. extend(defaultOptions, {
  41294. /**
  41295. * Maximum range which can be set using the navigator's handles.
  41296. * Opposite of [xAxis.minRange](#xAxis.minRange).
  41297. *
  41298. * @sample {highstock} stock/navigator/maxrange/
  41299. * Defined max and min range
  41300. *
  41301. * @type {number}
  41302. * @since 6.0.0
  41303. * @product highstock gantt
  41304. * @apioption xAxis.maxRange
  41305. */
  41306. /**
  41307. * The navigator is a small series below the main series, displaying
  41308. * a view of the entire data set. It provides tools to zoom in and
  41309. * out on parts of the data as well as panning across the dataset.
  41310. *
  41311. * @product highstock gantt
  41312. * @optionparent navigator
  41313. */
  41314. navigator: {
  41315. /**
  41316. * Whether the navigator and scrollbar should adapt to updated data
  41317. * in the base X axis. When loading data async, as in the demo below,
  41318. * this should be `false`. Otherwise new data will trigger navigator
  41319. * redraw, which will cause unwanted looping. In the demo below, the
  41320. * data in the navigator is set only once. On navigating, only the main
  41321. * chart content is updated.
  41322. *
  41323. * @sample {highstock} stock/demo/lazy-loading/
  41324. * Set to false with async data loading
  41325. *
  41326. * @type {boolean}
  41327. * @default true
  41328. * @apioption navigator.adaptToUpdatedData
  41329. */
  41330. /**
  41331. * An integer identifying the index to use for the base series, or a
  41332. * string representing the id of the series.
  41333. *
  41334. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  41335. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  41336. *
  41337. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  41338. *
  41339. * @deprecated
  41340. * @type {number|string}
  41341. * @default 0
  41342. * @apioption navigator.baseSeries
  41343. */
  41344. /**
  41345. * Enable or disable the navigator.
  41346. *
  41347. * @sample {highstock} stock/navigator/enabled/
  41348. * Disable the navigator
  41349. *
  41350. * @type {boolean}
  41351. * @default true
  41352. * @apioption navigator.enabled
  41353. */
  41354. /**
  41355. * When the chart is inverted, whether to draw the navigator on the
  41356. * opposite side.
  41357. *
  41358. * @type {boolean}
  41359. * @default false
  41360. * @since 5.0.8
  41361. * @apioption navigator.opposite
  41362. */
  41363. /**
  41364. * The height of the navigator.
  41365. *
  41366. * @sample {highstock} stock/navigator/height/
  41367. * A higher navigator
  41368. */
  41369. height: 40,
  41370. /**
  41371. * The distance from the nearest element, the X axis or X axis labels.
  41372. *
  41373. * @sample {highstock} stock/navigator/margin/
  41374. * A margin of 2 draws the navigator closer to the X axis labels
  41375. */
  41376. margin: 25,
  41377. /**
  41378. * Whether the mask should be inside the range marking the zoomed
  41379. * range, or outside. In Highstock 1.x it was always `false`.
  41380. *
  41381. * @sample {highstock} stock/navigator/maskinside-false/
  41382. * False, mask outside
  41383. *
  41384. * @since 2.0
  41385. */
  41386. maskInside: true,
  41387. /**
  41388. * Options for the handles for dragging the zoomed area.
  41389. *
  41390. * @sample {highstock} stock/navigator/handles/
  41391. * Colored handles
  41392. */
  41393. handles: {
  41394. /**
  41395. * Width for handles.
  41396. *
  41397. * @sample {highstock} stock/navigator/styled-handles/
  41398. * Styled handles
  41399. *
  41400. * @since 6.0.0
  41401. */
  41402. width: 7,
  41403. /**
  41404. * Height for handles.
  41405. *
  41406. * @sample {highstock} stock/navigator/styled-handles/
  41407. * Styled handles
  41408. *
  41409. * @since 6.0.0
  41410. */
  41411. height: 15,
  41412. /**
  41413. * Array to define shapes of handles. 0-index for left, 1-index for
  41414. * right.
  41415. *
  41416. * Additionally, the URL to a graphic can be given on this form:
  41417. * `url(graphic.png)`. Note that for the image to be applied to
  41418. * exported charts, its URL needs to be accessible by the export
  41419. * server.
  41420. *
  41421. * Custom callbacks for symbol path generation can also be added to
  41422. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  41423. * used by its method name, as shown in the demo.
  41424. *
  41425. * @sample {highstock} stock/navigator/styled-handles/
  41426. * Styled handles
  41427. *
  41428. * @type {Array<string>}
  41429. * @default ["navigator-handle", "navigator-handle"]
  41430. * @since 6.0.0
  41431. */
  41432. symbols: ['navigator-handle', 'navigator-handle'],
  41433. /**
  41434. * Allows to enable/disable handles.
  41435. *
  41436. * @since 6.0.0
  41437. */
  41438. enabled: true,
  41439. /**
  41440. * The width for the handle border and the stripes inside.
  41441. *
  41442. * @sample {highstock} stock/navigator/styled-handles/
  41443. * Styled handles
  41444. *
  41445. * @since 6.0.0
  41446. * @apioption navigator.handles.lineWidth
  41447. */
  41448. lineWidth: 1,
  41449. /**
  41450. * The fill for the handle.
  41451. *
  41452. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  41453. */
  41454. backgroundColor: '#f2f2f2',
  41455. /**
  41456. * The stroke for the handle border and the stripes inside.
  41457. *
  41458. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  41459. */
  41460. borderColor: '#999999'
  41461. },
  41462. /**
  41463. * The color of the mask covering the areas of the navigator series
  41464. * that are currently not visible in the main series. The default
  41465. * color is bluish with an opacity of 0.3 to see the series below.
  41466. *
  41467. * @see In styled mode, the mask is styled with the
  41468. * `.highcharts-navigator-mask` and
  41469. * `.highcharts-navigator-mask-inside` classes.
  41470. *
  41471. * @sample {highstock} stock/navigator/maskfill/
  41472. * Blue, semi transparent mask
  41473. *
  41474. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  41475. * @default rgba(102,133,194,0.3)
  41476. */
  41477. maskFill: color('#6685c2').setOpacity(0.3).get(),
  41478. /**
  41479. * The color of the line marking the currently zoomed area in the
  41480. * navigator.
  41481. *
  41482. * @sample {highstock} stock/navigator/outline/
  41483. * 2px blue outline
  41484. *
  41485. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  41486. * @default #cccccc
  41487. */
  41488. outlineColor: '#cccccc',
  41489. /**
  41490. * The width of the line marking the currently zoomed area in the
  41491. * navigator.
  41492. *
  41493. * @see In styled mode, the outline stroke width is set with the
  41494. * `.highcharts-navigator-outline` class.
  41495. *
  41496. * @sample {highstock} stock/navigator/outline/
  41497. * 2px blue outline
  41498. *
  41499. * @type {number}
  41500. */
  41501. outlineWidth: 1,
  41502. /**
  41503. * Options for the navigator series. Available options are the same
  41504. * as any series, documented at [plotOptions](#plotOptions.series)
  41505. * and [series](#series).
  41506. *
  41507. * Unless data is explicitly defined on navigator.series, the data
  41508. * is borrowed from the first series in the chart.
  41509. *
  41510. * Default series options for the navigator series are:
  41511. * ```js
  41512. * series: {
  41513. * type: 'areaspline',
  41514. * fillOpacity: 0.05,
  41515. * dataGrouping: {
  41516. * smoothed: true
  41517. * },
  41518. * lineWidth: 1,
  41519. * marker: {
  41520. * enabled: false
  41521. * }
  41522. * }
  41523. * ```
  41524. *
  41525. * @see In styled mode, the navigator series is styled with the
  41526. * `.highcharts-navigator-series` class.
  41527. *
  41528. * @sample {highstock} stock/navigator/series-data/
  41529. * Using a separate data set for the navigator
  41530. * @sample {highstock} stock/navigator/series/
  41531. * A green navigator series
  41532. *
  41533. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  41534. */
  41535. series: {
  41536. /**
  41537. * The type of the navigator series. Defaults to `areaspline` if
  41538. * defined, otherwise `line`.
  41539. *
  41540. * Heads up:
  41541. * In column-type navigator, zooming is limited to at least one
  41542. * point with its `pointRange`.
  41543. *
  41544. * @sample {highstock} stock/navigator/column/
  41545. * Column type navigator
  41546. *
  41547. * @type {string}
  41548. * @default areaspline
  41549. */
  41550. type: defaultSeriesType,
  41551. /**
  41552. * The fill opacity of the navigator series.
  41553. */
  41554. fillOpacity: 0.05,
  41555. /**
  41556. * The pixel line width of the navigator series.
  41557. */
  41558. lineWidth: 1,
  41559. /**
  41560. * @ignore-option
  41561. */
  41562. compare: null,
  41563. /**
  41564. * Unless data is explicitly defined, the data is borrowed from the
  41565. * first series in the chart.
  41566. *
  41567. * @type {Array<number|Array<number|string|null>|object|null>}
  41568. * @product highstock
  41569. * @apioption navigator.series.data
  41570. */
  41571. /**
  41572. * Data grouping options for the navigator series.
  41573. *
  41574. * @extends plotOptions.series.dataGrouping
  41575. */
  41576. dataGrouping: {
  41577. approximation: 'average',
  41578. enabled: true,
  41579. groupPixelWidth: 2,
  41580. smoothed: true,
  41581. // Day and week differs from plotOptions.series.dataGrouping
  41582. units: [
  41583. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  41584. ['second', [1, 2, 5, 10, 15, 30]],
  41585. ['minute', [1, 2, 5, 10, 15, 30]],
  41586. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  41587. ['day', [1, 2, 3, 4]],
  41588. ['week', [1, 2, 3]],
  41589. ['month', [1, 3, 6]],
  41590. ['year', null]
  41591. ]
  41592. },
  41593. /**
  41594. * Data label options for the navigator series. Data labels are
  41595. * disabled by default on the navigator series.
  41596. *
  41597. * @extends plotOptions.series.dataLabels
  41598. */
  41599. dataLabels: {
  41600. enabled: false,
  41601. zIndex: 2 // #1839
  41602. },
  41603. id: 'highcharts-navigator-series',
  41604. className: 'highcharts-navigator-series',
  41605. /**
  41606. * Sets the fill color of the navigator series.
  41607. *
  41608. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  41609. * @apioption navigator.series.color
  41610. */
  41611. /**
  41612. * Line color for the navigator series. Allows setting the color
  41613. * while disallowing the default candlestick setting.
  41614. *
  41615. * @type {Highcharts.ColorString|null}
  41616. */
  41617. lineColor: null,
  41618. marker: {
  41619. enabled: false
  41620. },
  41621. /**
  41622. * Since Highstock v8, default value is the same as default
  41623. * `pointRange` defined for a specific type (e.g. `null` for
  41624. * column type).
  41625. *
  41626. * In Highstock version < 8, defaults to 0.
  41627. *
  41628. * @extends plotOptions.series.pointRange
  41629. * @type {number|null}
  41630. * @apioption navigator.series.pointRange
  41631. */
  41632. /**
  41633. * The threshold option. Setting it to 0 will make the default
  41634. * navigator area series draw its area from the 0 value and up.
  41635. *
  41636. * @type {number|null}
  41637. */
  41638. threshold: null
  41639. },
  41640. /**
  41641. * Options for the navigator X axis. Default series options for the
  41642. * navigator xAxis are:
  41643. * ```js
  41644. * xAxis: {
  41645. * tickWidth: 0,
  41646. * lineWidth: 0,
  41647. * gridLineWidth: 1,
  41648. * tickPixelInterval: 200,
  41649. * labels: {
  41650. * align: 'left',
  41651. * style: {
  41652. * color: '#888'
  41653. * },
  41654. * x: 3,
  41655. * y: -4
  41656. * }
  41657. * }
  41658. * ```
  41659. *
  41660. * @extends xAxis
  41661. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  41662. * showEmpty, maxRange
  41663. */
  41664. xAxis: {
  41665. /**
  41666. * Additional range on the right side of the xAxis. Works similar to
  41667. * xAxis.maxPadding, but value is set in milliseconds.
  41668. * Can be set for both, main xAxis and navigator's xAxis.
  41669. *
  41670. * @since 6.0.0
  41671. */
  41672. overscroll: 0,
  41673. className: 'highcharts-navigator-xaxis',
  41674. tickLength: 0,
  41675. lineWidth: 0,
  41676. gridLineColor: '#e6e6e6',
  41677. gridLineWidth: 1,
  41678. tickPixelInterval: 200,
  41679. labels: {
  41680. align: 'left',
  41681. /**
  41682. * @type {Highcharts.CSSObject}
  41683. */
  41684. style: {
  41685. /** @ignore */
  41686. color: '#999999'
  41687. },
  41688. x: 3,
  41689. y: -4
  41690. },
  41691. crosshair: false
  41692. },
  41693. /**
  41694. * Options for the navigator Y axis. Default series options for the
  41695. * navigator yAxis are:
  41696. * ```js
  41697. * yAxis: {
  41698. * gridLineWidth: 0,
  41699. * startOnTick: false,
  41700. * endOnTick: false,
  41701. * minPadding: 0.1,
  41702. * maxPadding: 0.1,
  41703. * labels: {
  41704. * enabled: false
  41705. * },
  41706. * title: {
  41707. * text: null
  41708. * },
  41709. * tickWidth: 0
  41710. * }
  41711. * ```
  41712. *
  41713. * @extends yAxis
  41714. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  41715. * showEmpty, scrollbar, top, units, maxRange, minLength,
  41716. * maxLength, resize
  41717. */
  41718. yAxis: {
  41719. className: 'highcharts-navigator-yaxis',
  41720. gridLineWidth: 0,
  41721. startOnTick: false,
  41722. endOnTick: false,
  41723. minPadding: 0.1,
  41724. maxPadding: 0.1,
  41725. labels: {
  41726. enabled: false
  41727. },
  41728. crosshair: false,
  41729. title: {
  41730. text: null
  41731. },
  41732. tickLength: 0,
  41733. tickWidth: 0
  41734. }
  41735. }
  41736. });
  41737. /* eslint-disable no-invalid-this, valid-jsdoc */
  41738. /**
  41739. * Draw one of the handles on the side of the zoomed range in the navigator
  41740. *
  41741. * @private
  41742. * @function Highcharts.Renderer#symbols.navigator-handle
  41743. * @param {number} x
  41744. * @param {number} y
  41745. * @param {number} w
  41746. * @param {number} h
  41747. * @param {Highcharts.NavigatorHandlesOptions} options
  41748. * @return {Highcharts.SVGPathArray}
  41749. * Path to be used in a handle
  41750. */
  41751. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  41752. var halfWidth = options.width / 2, markerPosition = Math.round(halfWidth / 3) + 0.5, height = options.height;
  41753. return [
  41754. 'M',
  41755. -halfWidth - 1, 0.5,
  41756. 'L',
  41757. halfWidth, 0.5,
  41758. 'L',
  41759. halfWidth, height + 0.5,
  41760. 'L',
  41761. -halfWidth - 1, height + 0.5,
  41762. 'L',
  41763. -halfWidth - 1, 0.5,
  41764. 'M',
  41765. -markerPosition, 4,
  41766. 'L',
  41767. -markerPosition, height - 3,
  41768. 'M',
  41769. markerPosition - 1, 4,
  41770. 'L',
  41771. markerPosition - 1, height - 3
  41772. ];
  41773. };
  41774. /**
  41775. * Add logic to normalize the zoomed range in order to preserve the pressed
  41776. * state of range selector buttons
  41777. *
  41778. * @private
  41779. * @function Highcharts.Axis#toFixedRange
  41780. * @param {number} [pxMin]
  41781. * @param {number} [pxMax]
  41782. * @param {number} [fixedMin]
  41783. * @param {number} [fixedMax]
  41784. * @return {*}
  41785. */
  41786. Axis.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  41787. var fixedRange = this.chart && this.chart.fixedRange, halfPointRange = (this.pointRange || 0) / 2, newMin = pick(fixedMin, this.translate(pxMin, true, !this.horiz)), newMax = pick(fixedMax, this.translate(pxMax, true, !this.horiz)), changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  41788. // Add/remove half point range to/from the extremes (#1172)
  41789. if (!defined(fixedMin)) {
  41790. newMin = correctFloat(newMin + halfPointRange);
  41791. }
  41792. if (!defined(fixedMax)) {
  41793. newMax = correctFloat(newMax - halfPointRange);
  41794. }
  41795. // If the difference between the fixed range and the actual requested range
  41796. // is too great, the user is dragging across an ordinal gap, and we need to
  41797. // release the range selector button.
  41798. if (changeRatio > 0.7 && changeRatio < 1.3) {
  41799. if (fixedMax) {
  41800. newMin = newMax - fixedRange;
  41801. }
  41802. else {
  41803. newMax = newMin + fixedRange;
  41804. }
  41805. }
  41806. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  41807. newMin = newMax = void 0;
  41808. }
  41809. return {
  41810. min: newMin,
  41811. max: newMax
  41812. };
  41813. };
  41814. /**
  41815. * The Navigator class
  41816. *
  41817. * @private
  41818. * @class
  41819. * @name Highcharts.Navigator
  41820. *
  41821. * @param {Highcharts.Chart} chart
  41822. * Chart object
  41823. */
  41824. function Navigator(chart) {
  41825. this.init(chart);
  41826. }
  41827. Navigator.prototype = {
  41828. /**
  41829. * Draw one of the handles on the side of the zoomed range in the navigator
  41830. *
  41831. * @private
  41832. * @function Highcharts.Navigator#drawHandle
  41833. *
  41834. * @param {number} x
  41835. * The x center for the handle
  41836. *
  41837. * @param {number} index
  41838. * 0 for left and 1 for right
  41839. *
  41840. * @param {boolean|undefined} inverted
  41841. * flag for chart.inverted
  41842. *
  41843. * @param {string} verb
  41844. * use 'animate' or 'attr'
  41845. */
  41846. drawHandle: function (x, index, inverted, verb) {
  41847. var navigator = this, height = navigator.navigatorOptions.handles.height;
  41848. // Place it
  41849. navigator.handles[index][verb](inverted ? {
  41850. translateX: Math.round(navigator.left + navigator.height / 2),
  41851. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  41852. } : {
  41853. translateX: Math.round(navigator.left + parseInt(x, 10)),
  41854. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  41855. });
  41856. },
  41857. /**
  41858. * Render outline around the zoomed range
  41859. *
  41860. * @private
  41861. * @function Highcharts.Navigator#drawOutline
  41862. *
  41863. * @param {number} zoomedMin
  41864. * in pixels position where zoomed range starts
  41865. *
  41866. * @param {number} zoomedMax
  41867. * in pixels position where zoomed range ends
  41868. *
  41869. * @param {boolean|undefined} inverted
  41870. * flag if chart is inverted
  41871. *
  41872. * @param {string} verb
  41873. * use 'animate' or 'attr'
  41874. */
  41875. drawOutline: function (zoomedMin, zoomedMax, inverted, verb) {
  41876. var navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
  41877. outlineHeight = navigator.outlineHeight, scrollbarHeight = navigator.scrollbarHeight, navigatorSize = navigator.size, left = navigator.left - scrollbarHeight, navigatorTop = navigator.top, verticalMin, path;
  41878. if (inverted) {
  41879. left -= halfOutline;
  41880. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  41881. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  41882. path = [
  41883. 'M',
  41884. left + outlineHeight,
  41885. // top edge
  41886. navigatorTop - scrollbarHeight - outlineCorrection,
  41887. 'L',
  41888. left + outlineHeight,
  41889. verticalMin,
  41890. 'L',
  41891. left,
  41892. verticalMin,
  41893. 'L',
  41894. left,
  41895. zoomedMax,
  41896. 'L',
  41897. left + outlineHeight,
  41898. zoomedMax,
  41899. 'L',
  41900. left + outlineHeight,
  41901. // bottom edge
  41902. navigatorTop + navigatorSize + scrollbarHeight
  41903. ].concat(maskInside ? [
  41904. 'M',
  41905. left + outlineHeight,
  41906. verticalMin - halfOutline,
  41907. 'L',
  41908. left + outlineHeight,
  41909. zoomedMax + halfOutline // upper right of z.r.
  41910. ] : []);
  41911. }
  41912. else {
  41913. zoomedMin += left + scrollbarHeight - outlineCorrection;
  41914. zoomedMax += left + scrollbarHeight - outlineCorrection;
  41915. navigatorTop += halfOutline;
  41916. path = [
  41917. 'M',
  41918. left,
  41919. navigatorTop,
  41920. 'L',
  41921. zoomedMin,
  41922. navigatorTop,
  41923. 'L',
  41924. zoomedMin,
  41925. navigatorTop + outlineHeight,
  41926. 'L',
  41927. zoomedMax,
  41928. navigatorTop + outlineHeight,
  41929. 'L',
  41930. zoomedMax,
  41931. navigatorTop,
  41932. 'L',
  41933. left + navigatorSize + scrollbarHeight * 2,
  41934. navigatorTop // right
  41935. ].concat(maskInside ? [
  41936. 'M',
  41937. zoomedMin - halfOutline,
  41938. navigatorTop,
  41939. 'L',
  41940. zoomedMax + halfOutline,
  41941. navigatorTop // upper right of z.r.
  41942. ] : []);
  41943. }
  41944. navigator.outline[verb]({
  41945. d: path
  41946. });
  41947. },
  41948. /**
  41949. * Render outline around the zoomed range
  41950. *
  41951. * @private
  41952. * @function Highcharts.Navigator#drawMasks
  41953. *
  41954. * @param {number} zoomedMin
  41955. * in pixels position where zoomed range starts
  41956. *
  41957. * @param {number} zoomedMax
  41958. * in pixels position where zoomed range ends
  41959. *
  41960. * @param {boolean|undefined} inverted
  41961. * flag if chart is inverted
  41962. *
  41963. * @param {string} verb
  41964. * use 'animate' or 'attr'
  41965. */
  41966. drawMasks: function (zoomedMin, zoomedMax, inverted, verb) {
  41967. var navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height, height, width, x, y;
  41968. // Determine rectangle position & size
  41969. // According to (non)inverted position:
  41970. if (inverted) {
  41971. x = [left, left, left];
  41972. y = [top, top + zoomedMin, top + zoomedMax];
  41973. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  41974. height = [
  41975. zoomedMin,
  41976. zoomedMax - zoomedMin,
  41977. navigator.size - zoomedMax
  41978. ];
  41979. }
  41980. else {
  41981. x = [left, left + zoomedMin, left + zoomedMax];
  41982. y = [top, top, top];
  41983. width = [
  41984. zoomedMin,
  41985. zoomedMax - zoomedMin,
  41986. navigator.size - zoomedMax
  41987. ];
  41988. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  41989. }
  41990. navigator.shades.forEach(function (shade, i) {
  41991. shade[verb]({
  41992. x: x[i],
  41993. y: y[i],
  41994. width: width[i],
  41995. height: height[i]
  41996. });
  41997. });
  41998. },
  41999. /**
  42000. * Generate DOM elements for a navigator:
  42001. *
  42002. * - main navigator group
  42003. *
  42004. * - all shades
  42005. *
  42006. * - outline
  42007. *
  42008. * - handles
  42009. *
  42010. * @private
  42011. * @function Highcharts.Navigator#renderElements
  42012. */
  42013. renderElements: function () {
  42014. var navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, navigatorGroup, mouseCursor = {
  42015. cursor: inverted ? 'ns-resize' : 'ew-resize'
  42016. };
  42017. // Create the main navigator group
  42018. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  42019. .attr({
  42020. zIndex: 8,
  42021. visibility: 'hidden'
  42022. })
  42023. .add();
  42024. // Create masks, each mask will get events and fill:
  42025. [
  42026. !maskInside,
  42027. maskInside,
  42028. !maskInside
  42029. ].forEach(function (hasMask, index) {
  42030. navigator.shades[index] = renderer.rect()
  42031. .addClass('highcharts-navigator-mask' +
  42032. (index === 1 ? '-inside' : '-outside'))
  42033. .add(navigatorGroup);
  42034. if (!chart.styledMode) {
  42035. navigator.shades[index]
  42036. .attr({
  42037. fill: hasMask ?
  42038. navigatorOptions.maskFill :
  42039. 'rgba(0,0,0,0)'
  42040. })
  42041. .css((index === 1) && mouseCursor);
  42042. }
  42043. });
  42044. // Create the outline:
  42045. navigator.outline = renderer.path()
  42046. .addClass('highcharts-navigator-outline')
  42047. .add(navigatorGroup);
  42048. if (!chart.styledMode) {
  42049. navigator.outline.attr({
  42050. 'stroke-width': navigatorOptions.outlineWidth,
  42051. stroke: navigatorOptions.outlineColor
  42052. });
  42053. }
  42054. // Create the handlers:
  42055. if (navigatorOptions.handles.enabled) {
  42056. [0, 1].forEach(function (index) {
  42057. navigatorOptions.handles.inverted = chart.inverted;
  42058. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  42059. // zIndex = 6 for right handle, 7 for left.
  42060. // Can't be 10, because of the tooltip in inverted chart #2908
  42061. navigator.handles[index].attr({ zIndex: 7 - index })
  42062. .addClass('highcharts-navigator-handle ' +
  42063. 'highcharts-navigator-handle-' +
  42064. ['left', 'right'][index]).add(navigatorGroup);
  42065. if (!chart.styledMode) {
  42066. var handlesOptions = navigatorOptions.handles;
  42067. navigator.handles[index]
  42068. .attr({
  42069. fill: handlesOptions.backgroundColor,
  42070. stroke: handlesOptions.borderColor,
  42071. 'stroke-width': handlesOptions.lineWidth
  42072. })
  42073. .css(mouseCursor);
  42074. }
  42075. });
  42076. }
  42077. },
  42078. /**
  42079. * Update navigator
  42080. *
  42081. * @private
  42082. * @function Highcharts.Navigator#update
  42083. *
  42084. * @param {Highcharts.NavigatorOptions} options
  42085. * Options to merge in when updating navigator
  42086. */
  42087. update: function (options) {
  42088. // Remove references to old navigator series in base series
  42089. (this.series || []).forEach(function (series) {
  42090. if (series.baseSeries) {
  42091. delete series.baseSeries.navigatorSeries;
  42092. }
  42093. });
  42094. // Destroy and rebuild navigator
  42095. this.destroy();
  42096. var chartOptions = this.chart.options;
  42097. merge(true, chartOptions.navigator, this.options, options);
  42098. this.init(this.chart);
  42099. },
  42100. /**
  42101. * Render the navigator
  42102. *
  42103. * @private
  42104. * @function Highcharts.Navigator#render
  42105. * @param {number} min
  42106. * X axis value minimum
  42107. * @param {number} max
  42108. * X axis value maximum
  42109. * @param {number} [pxMin]
  42110. * Pixel value minimum
  42111. * @param {number} [pxMax]
  42112. * Pixel value maximum
  42113. * @return {void}
  42114. */
  42115. render: function (min, max, pxMin, pxMax) {
  42116. var navigator = this, chart = navigator.chart, navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, xAxis = navigator.xAxis, pointRange = xAxis.pointRange || 0, scrollbarXAxis = xAxis.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;
  42117. // Don't redraw while moving the handles (#4703).
  42118. if (this.hasDragged && !defined(pxMin)) {
  42119. return;
  42120. }
  42121. min = correctFloat(min - pointRange / 2);
  42122. max = correctFloat(max + pointRange / 2);
  42123. // Don't render the navigator until we have data (#486, #4202, #5172).
  42124. if (!isNumber(min) || !isNumber(max)) {
  42125. // However, if navigator was already rendered, we may need to resize
  42126. // it. For example hidden series, but visible navigator (#6022).
  42127. if (rendered) {
  42128. pxMin = 0;
  42129. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  42130. }
  42131. else {
  42132. return;
  42133. }
  42134. }
  42135. navigator.left = pick(xAxis.left,
  42136. // in case of scrollbar only, without navigator
  42137. chart.plotLeft + scrollbarHeight +
  42138. (inverted ? chart.plotWidth : 0));
  42139. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  42140. 2 * scrollbarHeight);
  42141. if (inverted) {
  42142. navigatorWidth = scrollbarHeight;
  42143. }
  42144. else {
  42145. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  42146. }
  42147. // Get the pixel position of the handles
  42148. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  42149. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  42150. // Verify (#1851, #2238)
  42151. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  42152. pxMin = 0;
  42153. pxMax = navigatorWidth;
  42154. }
  42155. // Are we below the minRange? (#2618, #6191)
  42156. newMin = xAxis.toValue(pxMin, true);
  42157. newMax = xAxis.toValue(pxMax, true);
  42158. currentRange = Math.abs(correctFloat(newMax - newMin));
  42159. if (currentRange < minRange) {
  42160. if (this.grabbedLeft) {
  42161. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  42162. }
  42163. else if (this.grabbedRight) {
  42164. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  42165. }
  42166. }
  42167. else if (defined(maxRange) &&
  42168. correctFloat(currentRange - pointRange) > maxRange) {
  42169. if (this.grabbedLeft) {
  42170. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  42171. }
  42172. else if (this.grabbedRight) {
  42173. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  42174. }
  42175. }
  42176. // Handles are allowed to cross, but never exceed the plot area
  42177. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  42178. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  42179. navigator.zoomedMax - navigator.fixedWidth :
  42180. Math.min(pxMin, pxMax), 0, zoomedMax);
  42181. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  42182. zoomedMax = Math.round(navigator.zoomedMax);
  42183. zoomedMin = Math.round(navigator.zoomedMin);
  42184. if (navigatorEnabled) {
  42185. navigator.navigatorGroup.attr({
  42186. visibility: 'visible'
  42187. });
  42188. // Place elements
  42189. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  42190. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  42191. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  42192. if (navigator.navigatorOptions.handles.enabled) {
  42193. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  42194. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  42195. }
  42196. }
  42197. if (navigator.scrollbar) {
  42198. if (inverted) {
  42199. scrollbarTop = navigator.top - scrollbarHeight;
  42200. scrollbarLeft = navigator.left - scrollbarHeight +
  42201. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  42202. // Multiple axes has offsets:
  42203. (scrollbarXAxis.titleOffset || 0) +
  42204. // Self margin from the axis.title
  42205. scrollbarXAxis.axisTitleMargin);
  42206. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  42207. }
  42208. else {
  42209. scrollbarTop = navigator.top + (navigatorEnabled ?
  42210. navigator.height :
  42211. -scrollbarHeight);
  42212. scrollbarLeft = navigator.left - scrollbarHeight;
  42213. }
  42214. // Reposition scrollbar
  42215. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  42216. // Keep scale 0-1
  42217. navigator.scrollbar.setRange(
  42218. // Use real value, not rounded because range can be very small
  42219. // (#1716)
  42220. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  42221. }
  42222. navigator.rendered = true;
  42223. },
  42224. /**
  42225. * Set up the mouse and touch events for the navigator
  42226. *
  42227. * @private
  42228. * @function Highcharts.Navigator#addMouseEvents
  42229. */
  42230. addMouseEvents: function () {
  42231. var navigator = this, chart = navigator.chart, container = chart.container, eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;
  42232. /**
  42233. * Create mouse events' handlers.
  42234. * Make them as separate functions to enable wrapping them:
  42235. */
  42236. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  42237. navigator.onMouseMove(e);
  42238. };
  42239. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  42240. navigator.onMouseUp(e);
  42241. };
  42242. // Add shades and handles mousedown events
  42243. eventsToUnbind = navigator.getPartsEvents('mousedown');
  42244. // Add mouse move and mouseup events. These are bind to doc/container,
  42245. // because Navigator.grabbedSomething flags are stored in mousedown
  42246. // events
  42247. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  42248. // Touch events
  42249. if (hasTouch) {
  42250. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  42251. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  42252. }
  42253. navigator.eventsToUnbind = eventsToUnbind;
  42254. // Data events
  42255. if (navigator.series && navigator.series[0]) {
  42256. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  42257. chart.navigator.modifyNavigatorAxisExtremes();
  42258. }));
  42259. }
  42260. },
  42261. /**
  42262. * Generate events for handles and masks
  42263. *
  42264. * @private
  42265. * @function Highcharts.Navigator#getPartsEvents
  42266. *
  42267. * @param {string} eventName
  42268. * Event name handler, 'mousedown' or 'touchstart'
  42269. *
  42270. * @return {Array<Function>}
  42271. * An array of functions to remove navigator functions from the
  42272. * events again.
  42273. */
  42274. getPartsEvents: function (eventName) {
  42275. var navigator = this, events = [];
  42276. ['shades', 'handles'].forEach(function (name) {
  42277. navigator[name].forEach(function (navigatorItem, index) {
  42278. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  42279. navigator[name + 'Mousedown'](e, index);
  42280. }));
  42281. });
  42282. });
  42283. return events;
  42284. },
  42285. /**
  42286. * Mousedown on a shaded mask, either:
  42287. *
  42288. * - will be stored for future drag&drop
  42289. *
  42290. * - will directly shift to a new range
  42291. *
  42292. * @private
  42293. * @function Highcharts.Navigator#shadesMousedown
  42294. *
  42295. * @param {Highcharts.PointerEventObject} e
  42296. * Mouse event
  42297. *
  42298. * @param {number} index
  42299. * Index of a mask in Navigator.shades array
  42300. */
  42301. shadesMousedown: function (e, index) {
  42302. e = this.chart.pointer.normalize(e);
  42303. 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;
  42304. // For inverted chart, swap some options:
  42305. if (chart.inverted) {
  42306. chartX = e.chartY;
  42307. navigatorPosition = navigator.top;
  42308. }
  42309. if (index === 1) {
  42310. // Store information for drag&drop
  42311. navigator.grabbedCenter = chartX;
  42312. navigator.fixedWidth = range;
  42313. navigator.dragOffset = chartX - zoomedMin;
  42314. }
  42315. else {
  42316. // Shift the range by clicking on shaded areas
  42317. left = chartX - navigatorPosition - range / 2;
  42318. if (index === 0) {
  42319. left = Math.max(0, left);
  42320. }
  42321. else if (index === 2 && left + range >= navigatorSize) {
  42322. left = navigatorSize - range;
  42323. if (navigator.reversedExtremes) {
  42324. // #7713
  42325. left -= range;
  42326. fixedMin = navigator.getUnionExtremes().dataMin;
  42327. }
  42328. else {
  42329. // #2293, #3543
  42330. fixedMax = navigator.getUnionExtremes().dataMax;
  42331. }
  42332. }
  42333. if (left !== zoomedMin) { // it has actually moved
  42334. navigator.fixedWidth = range; // #1370
  42335. ext = xAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  42336. if (defined(ext.min)) { // #7411
  42337. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  42338. { trigger: 'navigator' });
  42339. }
  42340. }
  42341. }
  42342. },
  42343. /**
  42344. * Mousedown on a handle mask.
  42345. * Will store necessary information for drag&drop.
  42346. *
  42347. * @private
  42348. * @function Highcharts.Navigator#handlesMousedown
  42349. * @param {Highcharts.PointerEventObject} e
  42350. * Mouse event
  42351. * @param {number} index
  42352. * Index of a handle in Navigator.handles array
  42353. * @return {void}
  42354. */
  42355. handlesMousedown: function (e, index) {
  42356. e = this.chart.pointer.normalize(e);
  42357. var navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0],
  42358. // For reversed axes, min and max are changed,
  42359. // so the other extreme should be stored
  42360. reverse = navigator.reversedExtremes;
  42361. if (index === 0) {
  42362. // Grab the left handle
  42363. navigator.grabbedLeft = true;
  42364. navigator.otherHandlePos = navigator.zoomedMax;
  42365. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  42366. }
  42367. else {
  42368. // Grab the right handle
  42369. navigator.grabbedRight = true;
  42370. navigator.otherHandlePos = navigator.zoomedMin;
  42371. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  42372. }
  42373. chart.fixedRange = null;
  42374. },
  42375. /**
  42376. * Mouse move event based on x/y mouse position.
  42377. *
  42378. * @private
  42379. * @function Highcharts.Navigator#onMouseMove
  42380. *
  42381. * @param {Highcharts.PointerEventObject} e
  42382. * Mouse event
  42383. */
  42384. onMouseMove: function (e) {
  42385. var navigator = this, chart = navigator.chart, left = navigator.left, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted, chartX;
  42386. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  42387. // the finger down in the center of the scrollbar. This should be
  42388. // ignored.
  42389. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  42390. e = chart.pointer.normalize(e);
  42391. chartX = e.chartX;
  42392. // Swap some options for inverted chart
  42393. if (inverted) {
  42394. left = navigator.top;
  42395. chartX = e.chartY;
  42396. }
  42397. // Drag left handle or top handle
  42398. if (navigator.grabbedLeft) {
  42399. navigator.hasDragged = true;
  42400. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  42401. // Drag right handle or bottom handle
  42402. }
  42403. else if (navigator.grabbedRight) {
  42404. navigator.hasDragged = true;
  42405. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  42406. // Drag scrollbar or open area in navigator
  42407. }
  42408. else if (navigator.grabbedCenter) {
  42409. navigator.hasDragged = true;
  42410. if (chartX < dragOffset) { // outside left
  42411. chartX = dragOffset;
  42412. // outside right
  42413. }
  42414. else if (chartX >
  42415. navigatorSize + dragOffset - range) {
  42416. chartX = navigatorSize + dragOffset - range;
  42417. }
  42418. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  42419. }
  42420. if (navigator.hasDragged &&
  42421. navigator.scrollbar &&
  42422. pick(navigator.scrollbar.options.liveRedraw,
  42423. // By default, don't run live redraw on VML, on touch
  42424. // devices or if the chart is in boost.
  42425. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  42426. e.DOMType = e.type; // DOMType is for IE8
  42427. setTimeout(function () {
  42428. navigator.onMouseUp(e);
  42429. }, 0);
  42430. }
  42431. }
  42432. },
  42433. /**
  42434. * Mouse up event based on x/y mouse position.
  42435. *
  42436. * @private
  42437. * @function Highcharts.Navigator#onMouseUp
  42438. * @param {Highcharts.PointerEventObject} e
  42439. * Mouse event
  42440. * @return {void}
  42441. */
  42442. onMouseUp: function (e) {
  42443. var navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, unionExtremes, fixedMin, fixedMax, ext, DOMEvent = e.DOMEvent || e;
  42444. if (
  42445. // MouseUp is called for both, navigator and scrollbar (that order),
  42446. // which causes calling afterSetExtremes twice. Prevent first call
  42447. // by checking if scrollbar is going to set new extremes (#6334)
  42448. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  42449. e.trigger === 'scrollbar') {
  42450. unionExtremes = navigator.getUnionExtremes();
  42451. // When dragging one handle, make sure the other one doesn't change
  42452. if (navigator.zoomedMin === navigator.otherHandlePos) {
  42453. fixedMin = navigator.fixedExtreme;
  42454. }
  42455. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  42456. fixedMax = navigator.fixedExtreme;
  42457. }
  42458. // Snap to right edge (#4076)
  42459. if (navigator.zoomedMax === navigator.size) {
  42460. fixedMax = navigator.reversedExtremes ?
  42461. unionExtremes.dataMin :
  42462. unionExtremes.dataMax;
  42463. }
  42464. // Snap to left edge (#7576)
  42465. if (navigator.zoomedMin === 0) {
  42466. fixedMin = navigator.reversedExtremes ?
  42467. unionExtremes.dataMax :
  42468. unionExtremes.dataMin;
  42469. }
  42470. ext = xAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  42471. if (defined(ext.min)) {
  42472. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  42473. // Run animation when clicking buttons, scrollbar track etc,
  42474. // but not when dragging handles or scrollbar
  42475. navigator.hasDragged ? false : null, {
  42476. trigger: 'navigator',
  42477. triggerOp: 'navigator-drag',
  42478. DOMEvent: DOMEvent // #1838
  42479. });
  42480. }
  42481. }
  42482. if (e.DOMType !== 'mousemove' &&
  42483. e.DOMType !== 'touchmove') {
  42484. navigator.grabbedLeft = navigator.grabbedRight =
  42485. navigator.grabbedCenter = navigator.fixedWidth =
  42486. navigator.fixedExtreme = navigator.otherHandlePos =
  42487. navigator.hasDragged = navigator.dragOffset = null;
  42488. }
  42489. },
  42490. /**
  42491. * Removes the event handlers attached previously with addEvents.
  42492. *
  42493. * @private
  42494. * @function Highcharts.Navigator#removeEvents
  42495. * @return {void}
  42496. */
  42497. removeEvents: function () {
  42498. if (this.eventsToUnbind) {
  42499. this.eventsToUnbind.forEach(function (unbind) {
  42500. unbind();
  42501. });
  42502. this.eventsToUnbind = void 0;
  42503. }
  42504. this.removeBaseSeriesEvents();
  42505. },
  42506. /**
  42507. * Remove data events.
  42508. *
  42509. * @private
  42510. * @function Highcharts.Navigator#removeBaseSeriesEvents
  42511. * @return {void}
  42512. */
  42513. removeBaseSeriesEvents: function () {
  42514. var baseSeries = this.baseSeries || [];
  42515. if (this.navigatorEnabled && baseSeries[0]) {
  42516. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  42517. baseSeries.forEach(function (series) {
  42518. removeEvent(series, 'updatedData', this.updatedDataHandler);
  42519. }, this);
  42520. }
  42521. // We only listen for extremes-events on the first baseSeries
  42522. if (baseSeries[0].xAxis) {
  42523. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  42524. }
  42525. }
  42526. },
  42527. /**
  42528. * Initialize the Navigator object
  42529. *
  42530. * @private
  42531. * @function Highcharts.Navigator#init
  42532. *
  42533. * @param {Highcharts.Chart} chart
  42534. */
  42535. init: function (chart) {
  42536. var chartOptions = chart.options, navigatorOptions = chartOptions.navigator, navigatorEnabled = navigatorOptions.enabled, scrollbarOptions = chartOptions.scrollbar, scrollbarEnabled = scrollbarOptions.enabled, height = navigatorEnabled ? navigatorOptions.height : 0, scrollbarHeight = scrollbarEnabled ?
  42537. scrollbarOptions.height :
  42538. 0;
  42539. this.handles = [];
  42540. this.shades = [];
  42541. this.chart = chart;
  42542. this.setBaseSeries();
  42543. this.height = height;
  42544. this.scrollbarHeight = scrollbarHeight;
  42545. this.scrollbarEnabled = scrollbarEnabled;
  42546. this.navigatorEnabled = navigatorEnabled;
  42547. this.navigatorOptions = navigatorOptions;
  42548. this.scrollbarOptions = scrollbarOptions;
  42549. this.outlineHeight = height + scrollbarHeight;
  42550. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  42551. var navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  42552. chart.xAxis[0] || { options: {} };
  42553. chart.isDirtyBox = true;
  42554. if (navigator.navigatorEnabled) {
  42555. // an x axis is required for scrollbar also
  42556. navigator.xAxis = new Axis(chart, merge({
  42557. // inherit base xAxis' break and ordinal options
  42558. breaks: baseXaxis.options.breaks,
  42559. ordinal: baseXaxis.options.ordinal
  42560. }, navigatorOptions.xAxis, {
  42561. id: 'navigator-x-axis',
  42562. yAxis: 'navigator-y-axis',
  42563. isX: true,
  42564. type: 'datetime',
  42565. index: xAxisIndex,
  42566. isInternal: true,
  42567. offset: 0,
  42568. keepOrdinalPadding: true,
  42569. startOnTick: false,
  42570. endOnTick: false,
  42571. minPadding: 0,
  42572. maxPadding: 0,
  42573. zoomEnabled: false
  42574. }, chart.inverted ? {
  42575. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  42576. width: height
  42577. } : {
  42578. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  42579. height: height
  42580. }));
  42581. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  42582. id: 'navigator-y-axis',
  42583. alignTicks: false,
  42584. offset: 0,
  42585. index: yAxisIndex,
  42586. isInternal: true,
  42587. zoomEnabled: false
  42588. }, chart.inverted ? {
  42589. width: height
  42590. } : {
  42591. height: height
  42592. }));
  42593. // If we have a base series, initialize the navigator series
  42594. if (baseSeries || navigatorOptions.series.data) {
  42595. navigator.updateNavigatorSeries(false);
  42596. // If not, set up an event to listen for added series
  42597. }
  42598. else if (chart.series.length === 0) {
  42599. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  42600. // We've got one, now add it as base
  42601. if (chart.series.length > 0 && !navigator.series) {
  42602. navigator.setBaseSeries();
  42603. navigator.unbindRedraw(); // reset
  42604. }
  42605. });
  42606. }
  42607. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  42608. // Render items, so we can bind events to them:
  42609. navigator.renderElements();
  42610. // Add mouse events
  42611. navigator.addMouseEvents();
  42612. // in case of scrollbar only, fake an x axis to get translation
  42613. }
  42614. else {
  42615. navigator.xAxis = {
  42616. translate: function (value, reverse) {
  42617. 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;
  42618. return reverse ?
  42619. // from pixel to value
  42620. (value * valueRange / scrollTrackWidth) + min :
  42621. // from value to pixel
  42622. scrollTrackWidth * (value - min) / valueRange;
  42623. },
  42624. toPixels: function (value) {
  42625. return this.translate(value);
  42626. },
  42627. toValue: function (value) {
  42628. return this.translate(value, true);
  42629. },
  42630. toFixedRange: Axis.prototype.toFixedRange,
  42631. fake: true
  42632. };
  42633. }
  42634. // Initialize the scrollbar
  42635. if (chart.options.scrollbar.enabled) {
  42636. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  42637. margin: navigator.navigatorEnabled ? 0 : 10,
  42638. vertical: chart.inverted
  42639. }), chart);
  42640. addEvent(navigator.scrollbar, 'changed', function (e) {
  42641. var range = navigator.size, to = range * this.to, from = range * this.from;
  42642. navigator.hasDragged = navigator.scrollbar.hasDragged;
  42643. navigator.render(0, 0, from, to);
  42644. if (chart.options.scrollbar.liveRedraw ||
  42645. (e.DOMType !== 'mousemove' &&
  42646. e.DOMType !== 'touchmove')) {
  42647. setTimeout(function () {
  42648. navigator.onMouseUp(e);
  42649. });
  42650. }
  42651. });
  42652. }
  42653. // Add data events
  42654. navigator.addBaseSeriesEvents();
  42655. // Add redraw events
  42656. navigator.addChartEvents();
  42657. },
  42658. /**
  42659. * Get the union data extremes of the chart - the outer data extremes of the
  42660. * base X axis and the navigator axis.
  42661. *
  42662. * @private
  42663. * @function Highcharts.Navigator#getUnionExtremes
  42664. * @param {boolean} [returnFalseOnNoBaseSeries]
  42665. * as the param says.
  42666. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  42667. */
  42668. getUnionExtremes: function (returnFalseOnNoBaseSeries) {
  42669. var baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options, ret;
  42670. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  42671. ret = {
  42672. dataMin: pick(// #4053
  42673. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  42674. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  42675. };
  42676. }
  42677. return ret;
  42678. },
  42679. /**
  42680. * Set the base series and update the navigator series from this. With a bit
  42681. * of modification we should be able to make this an API method to be called
  42682. * from the outside
  42683. *
  42684. * @private
  42685. * @function Highcharts.Navigator#setBaseSeries
  42686. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  42687. * Additional series options for a navigator
  42688. * @param {boolean} [redraw]
  42689. * Whether to redraw after update.
  42690. * @return {void}
  42691. */
  42692. setBaseSeries: function (baseSeriesOptions, redraw) {
  42693. var chart = this.chart, baseSeries = this.baseSeries = [];
  42694. baseSeriesOptions = (baseSeriesOptions ||
  42695. chart.options && chart.options.navigator.baseSeries ||
  42696. (chart.series.length ?
  42697. // Find the first non-navigator series (#8430)
  42698. H.find(chart.series, function (s) {
  42699. return !s.options.isInternal;
  42700. }).index :
  42701. 0));
  42702. // Iterate through series and add the ones that should be shown in
  42703. // navigator.
  42704. (chart.series || []).forEach(function (series, i) {
  42705. if (
  42706. // Don't include existing nav series
  42707. !series.options.isInternal &&
  42708. (series.options.showInNavigator ||
  42709. (i === baseSeriesOptions ||
  42710. series.options.id === baseSeriesOptions) &&
  42711. series.options.showInNavigator !== false)) {
  42712. baseSeries.push(series);
  42713. }
  42714. });
  42715. // When run after render, this.xAxis already exists
  42716. if (this.xAxis && !this.xAxis.fake) {
  42717. this.updateNavigatorSeries(true, redraw);
  42718. }
  42719. },
  42720. /**
  42721. * Update series in the navigator from baseSeries, adding new if does not
  42722. * exist.
  42723. *
  42724. * @private
  42725. * @function Highcharts.Navigator.updateNavigatorSeries
  42726. * @param {boolean} addEvents
  42727. * @param {boolean} [redraw]
  42728. * @return {void}
  42729. */
  42730. updateNavigatorSeries: function (addEvents, redraw) {
  42731. var navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions, navSeriesMixin = {
  42732. enableMouseTracking: false,
  42733. index: null,
  42734. linkedTo: null,
  42735. group: 'nav',
  42736. padXAxis: false,
  42737. xAxis: 'navigator-x-axis',
  42738. yAxis: 'navigator-y-axis',
  42739. showInLegend: false,
  42740. stacking: false,
  42741. isInternal: true,
  42742. states: {
  42743. inactive: {
  42744. opacity: 1
  42745. }
  42746. }
  42747. },
  42748. // Remove navigator series that are no longer in the baseSeries
  42749. navigatorSeries = navigator.series =
  42750. (navigator.series || []).filter(function (navSeries) {
  42751. var base = navSeries.baseSeries;
  42752. if (baseSeries.indexOf(base) < 0) { // Not in array
  42753. // If there is still a base series connected to this
  42754. // series, remove event handler and reference.
  42755. if (base) {
  42756. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  42757. delete base.navigatorSeries;
  42758. }
  42759. // Kill the nav series. It may already have been
  42760. // destroyed (#8715).
  42761. if (navSeries.chart) {
  42762. navSeries.destroy();
  42763. }
  42764. return false;
  42765. }
  42766. return true;
  42767. });
  42768. // Go through each base series and merge the options to create new
  42769. // series
  42770. if (baseSeries && baseSeries.length) {
  42771. baseSeries.forEach(function eachBaseSeries(base) {
  42772. var linkedNavSeries = base.navigatorSeries, userNavOptions = extend(
  42773. // Grab color and visibility from base as default
  42774. {
  42775. color: base.color,
  42776. visible: base.visible
  42777. }, !isArray(chartNavigatorSeriesOptions) ?
  42778. chartNavigatorSeriesOptions :
  42779. defaultOptions.navigator.series);
  42780. // Don't update if the series exists in nav and we have disabled
  42781. // adaptToUpdatedData.
  42782. if (linkedNavSeries &&
  42783. navigator.navigatorOptions.adaptToUpdatedData === false) {
  42784. return;
  42785. }
  42786. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  42787. baseOptions = base.options || {};
  42788. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  42789. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  42790. // Once nav series type is resolved, pick correct pointRange
  42791. mergedNavSeriesOptions.pointRange = pick(
  42792. // Stricte set pointRange in options
  42793. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  42794. // Fallback to default values, e.g. `null` for column
  42795. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  42796. // Merge data separately. Do a slice to avoid mutating the
  42797. // navigator options from base series (#4923).
  42798. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  42799. navigator.hasNavigatorData =
  42800. navigator.hasNavigatorData || !!navigatorSeriesData;
  42801. mergedNavSeriesOptions.data =
  42802. navigatorSeriesData ||
  42803. baseOptions.data && baseOptions.data.slice(0);
  42804. // Update or add the series
  42805. if (linkedNavSeries && linkedNavSeries.options) {
  42806. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  42807. }
  42808. else {
  42809. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  42810. base.navigatorSeries.baseSeries = base; // Store ref
  42811. navigatorSeries.push(base.navigatorSeries);
  42812. }
  42813. });
  42814. }
  42815. // If user has defined data (and no base series) or explicitly defined
  42816. // navigator.series as an array, we create these series on top of any
  42817. // base series.
  42818. if (chartNavigatorSeriesOptions.data &&
  42819. !(baseSeries && baseSeries.length) ||
  42820. isArray(chartNavigatorSeriesOptions)) {
  42821. navigator.hasNavigatorData = false;
  42822. // Allow navigator.series to be an array
  42823. chartNavigatorSeriesOptions =
  42824. splat(chartNavigatorSeriesOptions);
  42825. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  42826. navSeriesMixin.name =
  42827. 'Navigator ' + (navigatorSeries.length + 1);
  42828. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  42829. // Since we don't have a base series to pull color from,
  42830. // try to fake it by using color from series with same
  42831. // index. Otherwise pull from the colors array. We need
  42832. // an explicit color as otherwise updates will increment
  42833. // color counter and we'll get a new color for each
  42834. // update of the nav series.
  42835. color: chart.series[i] &&
  42836. !chart.series[i].options.isInternal &&
  42837. chart.series[i].color ||
  42838. chart.options.colors[i] ||
  42839. chart.options.colors[0]
  42840. }, navSeriesMixin, userSeriesOptions);
  42841. mergedNavSeriesOptions.data = userSeriesOptions.data;
  42842. if (mergedNavSeriesOptions.data) {
  42843. navigator.hasNavigatorData = true;
  42844. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  42845. }
  42846. });
  42847. }
  42848. if (addEvents) {
  42849. this.addBaseSeriesEvents();
  42850. }
  42851. },
  42852. /**
  42853. * Add data events.
  42854. * For example when main series is updated we need to recalculate extremes
  42855. *
  42856. * @private
  42857. * @function Highcharts.Navigator#addBaseSeriesEvent
  42858. * @return {void}
  42859. */
  42860. addBaseSeriesEvents: function () {
  42861. var navigator = this, baseSeries = navigator.baseSeries || [];
  42862. // Bind modified extremes event to first base's xAxis only.
  42863. // In event of > 1 base-xAxes, the navigator will ignore those.
  42864. // Adding this multiple times to the same axis is no problem, as
  42865. // duplicates should be discarded by the browser.
  42866. if (baseSeries[0] && baseSeries[0].xAxis) {
  42867. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  42868. }
  42869. baseSeries.forEach(function (base) {
  42870. // Link base series show/hide to navigator series visibility
  42871. addEvent(base, 'show', function () {
  42872. if (this.navigatorSeries) {
  42873. this.navigatorSeries.setVisible(true, false);
  42874. }
  42875. });
  42876. addEvent(base, 'hide', function () {
  42877. if (this.navigatorSeries) {
  42878. this.navigatorSeries.setVisible(false, false);
  42879. }
  42880. });
  42881. // Respond to updated data in the base series, unless explicitily
  42882. // not adapting to data changes.
  42883. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  42884. if (base.xAxis) {
  42885. addEvent(base, 'updatedData', this.updatedDataHandler);
  42886. }
  42887. }
  42888. // Handle series removal
  42889. addEvent(base, 'remove', function () {
  42890. if (this.navigatorSeries) {
  42891. erase(navigator.series, this.navigatorSeries);
  42892. if (defined(this.navigatorSeries.options)) {
  42893. this.navigatorSeries.remove(false);
  42894. }
  42895. delete this.navigatorSeries;
  42896. }
  42897. });
  42898. }, this);
  42899. },
  42900. /**
  42901. * Get minimum from all base series connected to the navigator
  42902. * @private
  42903. * @param {number} currentSeriesMin
  42904. * Minium from the current series
  42905. * @return {number} Minimum from all series
  42906. */
  42907. getBaseSeriesMin: function (currentSeriesMin) {
  42908. return this.baseSeries.reduce(function (min, series) {
  42909. // (#10193)
  42910. return Math.min(min, series.xData ? series.xData[0] : min);
  42911. }, currentSeriesMin);
  42912. },
  42913. /**
  42914. * Set the navigator x axis extremes to reflect the total. The navigator
  42915. * extremes should always be the extremes of the union of all series in the
  42916. * chart as well as the navigator series.
  42917. *
  42918. * @private
  42919. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  42920. */
  42921. modifyNavigatorAxisExtremes: function () {
  42922. var xAxis = this.xAxis, unionExtremes;
  42923. if (typeof xAxis.getExtremes !== 'undefined') {
  42924. unionExtremes = this.getUnionExtremes(true);
  42925. if (unionExtremes &&
  42926. (unionExtremes.dataMin !== xAxis.min ||
  42927. unionExtremes.dataMax !== xAxis.max)) {
  42928. xAxis.min = unionExtremes.dataMin;
  42929. xAxis.max = unionExtremes.dataMax;
  42930. }
  42931. }
  42932. },
  42933. /**
  42934. * Hook to modify the base axis extremes with information from the Navigator
  42935. *
  42936. * @private
  42937. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  42938. */
  42939. modifyBaseAxisExtremes: function () {
  42940. 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,
  42941. // When the extremes have been set by range selector button, don't
  42942. // stick to min or max. The range selector buttons will handle the
  42943. // extremes. (#5489)
  42944. unmutable = baseXAxis.eventArgs &&
  42945. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  42946. if (!unmutable) {
  42947. // If the zoomed range is already at the min, move it to the right
  42948. // as new data comes in
  42949. if (stickToMin) {
  42950. newMin = baseDataMin;
  42951. newMax = newMin + range;
  42952. }
  42953. // If the zoomed range is already at the max, move it to the right
  42954. // as new data comes in
  42955. if (stickToMax) {
  42956. newMax = baseDataMax + overscroll;
  42957. // if stickToMin is true, the new min value is set above
  42958. if (!stickToMin) {
  42959. newMin = Math.max(newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  42960. navigatorSeries.xData[0] :
  42961. -Number.MAX_VALUE));
  42962. }
  42963. }
  42964. // Update the extremes
  42965. if (hasSetExtremes && (stickToMin || stickToMax)) {
  42966. if (isNumber(newMin)) {
  42967. baseXAxis.min = baseXAxis.userMin = newMin;
  42968. baseXAxis.max = baseXAxis.userMax = newMax;
  42969. }
  42970. }
  42971. }
  42972. // Reset
  42973. navigator.stickToMin =
  42974. navigator.stickToMax = null;
  42975. },
  42976. /**
  42977. * Handler for updated data on the base series. When data is modified, the
  42978. * navigator series must reflect it. This is called from the Chart.redraw
  42979. * function before axis and series extremes are computed.
  42980. *
  42981. * @private
  42982. * @function Highcharts.Navigator#updateDataHandler
  42983. */
  42984. updatedDataHandler: function () {
  42985. var navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  42986. // If the scrollbar is scrolled all the way to the right, keep right as
  42987. // new data comes in.
  42988. navigator.stickToMax = navigator.reversedExtremes ?
  42989. Math.round(navigator.zoomedMin) === 0 :
  42990. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  42991. // Detect whether the zoomed area should stick to the minimum or
  42992. // maximum. If the current axis minimum falls outside the new updated
  42993. // dataset, we must adjust.
  42994. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  42995. (baseSeries.xAxis.min <= xDataMin) &&
  42996. (!this.chart.fixedRange || !navigator.stickToMax);
  42997. // Set the navigator series data to the new data of the base series
  42998. if (navigatorSeries && !navigator.hasNavigatorData) {
  42999. navigatorSeries.options.pointStart = baseSeries.xData[0];
  43000. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  43001. }
  43002. },
  43003. /**
  43004. * Add chart events, like redrawing navigator, when chart requires that.
  43005. *
  43006. * @private
  43007. * @function Highcharts.Navigator#addChartEvents
  43008. * @return {void}
  43009. */
  43010. addChartEvents: function () {
  43011. if (!this.eventsToUnbind) {
  43012. this.eventsToUnbind = [];
  43013. }
  43014. this.eventsToUnbind.push(
  43015. // Move the scrollbar after redraw, like after data updata even if
  43016. // axes don't redraw
  43017. addEvent(this.chart, 'redraw', function () {
  43018. var navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&
  43019. navigator.baseSeries[0] &&
  43020. navigator.baseSeries[0].xAxis ||
  43021. navigator.scrollbar && this.xAxis[0]); // #5709
  43022. if (xAxis) {
  43023. navigator.render(xAxis.min, xAxis.max);
  43024. }
  43025. }),
  43026. // Make room for the navigator, can be placed around the chart:
  43027. addEvent(this.chart, 'getMargins', function () {
  43028. var chart = this, navigator = chart.navigator, marginName = navigator.opposite ?
  43029. 'plotTop' : 'marginBottom';
  43030. if (chart.inverted) {
  43031. marginName = navigator.opposite ?
  43032. 'marginRight' : 'plotLeft';
  43033. }
  43034. chart[marginName] =
  43035. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  43036. navigator.outlineHeight :
  43037. 0) + navigator.navigatorOptions.margin;
  43038. }));
  43039. },
  43040. /**
  43041. * Destroys allocated elements.
  43042. *
  43043. * @private
  43044. * @function Highcharts.Navigator#destroy
  43045. */
  43046. destroy: function () {
  43047. // Disconnect events added in addEvents
  43048. this.removeEvents();
  43049. if (this.xAxis) {
  43050. erase(this.chart.xAxis, this.xAxis);
  43051. erase(this.chart.axes, this.xAxis);
  43052. }
  43053. if (this.yAxis) {
  43054. erase(this.chart.yAxis, this.yAxis);
  43055. erase(this.chart.axes, this.yAxis);
  43056. }
  43057. // Destroy series
  43058. (this.series || []).forEach(function (s) {
  43059. if (s.destroy) {
  43060. s.destroy();
  43061. }
  43062. });
  43063. // Destroy properties
  43064. [
  43065. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  43066. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  43067. 'rendered'
  43068. ].forEach(function (prop) {
  43069. if (this[prop] && this[prop].destroy) {
  43070. this[prop].destroy();
  43071. }
  43072. this[prop] = null;
  43073. }, this);
  43074. // Destroy elements in collection
  43075. [this.handles].forEach(function (coll) {
  43076. destroyObjectProperties(coll);
  43077. }, this);
  43078. }
  43079. };
  43080. if (!H.Navigator) {
  43081. H.Navigator = Navigator;
  43082. // For Stock charts, override selection zooming with some special features
  43083. // because X axis zooming is already allowed by the Navigator and Range
  43084. // selector.
  43085. addEvent(Axis, 'zoom', function (e) {
  43086. var chart = this.chart, chartOptions = chart.options, zoomType = chartOptions.chart.zoomType, pinchType = chartOptions.chart.pinchType, previousZoom, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  43087. if (this.isXAxis && ((navigator && navigator.enabled) ||
  43088. (rangeSelector && rangeSelector.enabled))) {
  43089. // For y only zooming, ignore the X axis completely
  43090. if (zoomType === 'y') {
  43091. e.zoomed = false;
  43092. // For xy zooming, record the state of the zoom before zoom
  43093. // selection, then when the reset button is pressed, revert to this
  43094. // state. This should apply only if the chart is initialized with a
  43095. // range (#6612), otherwise zoom all the way out.
  43096. }
  43097. else if (((!isTouchDevice && zoomType === 'xy') ||
  43098. (isTouchDevice && pinchType === 'xy')) &&
  43099. this.options.range) {
  43100. previousZoom = this.previousZoom;
  43101. if (defined(e.newMin)) {
  43102. this.previousZoom = [this.min, this.max];
  43103. }
  43104. else if (previousZoom) {
  43105. e.newMin = previousZoom[0];
  43106. e.newMax = previousZoom[1];
  43107. delete this.previousZoom;
  43108. }
  43109. }
  43110. }
  43111. if (typeof e.zoomed !== 'undefined') {
  43112. e.preventDefault();
  43113. }
  43114. });
  43115. // For Stock charts. For x only zooming, do not to create the zoom button
  43116. // because X axis zooming is already allowed by the Navigator and Range
  43117. // selector. (#9285)
  43118. addEvent(Chart, 'beforeShowResetZoom', function () {
  43119. var chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  43120. if (((navigator && navigator.enabled) ||
  43121. (rangeSelector && rangeSelector.enabled)) &&
  43122. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  43123. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  43124. return false;
  43125. }
  43126. });
  43127. // Initialize navigator for stock charts
  43128. addEvent(Chart, 'beforeRender', function () {
  43129. var options = this.options;
  43130. if (options.navigator.enabled ||
  43131. options.scrollbar.enabled) {
  43132. this.scroller = this.navigator = new Navigator(this);
  43133. }
  43134. });
  43135. // For stock charts, extend the Chart.setChartSize method so that we can set
  43136. // the final top position of the navigator once the height of the chart,
  43137. // including the legend, is determined. #367. We can't use Chart.getMargins,
  43138. // because labels offsets are not calculated yet.
  43139. addEvent(Chart, 'afterSetChartSize', function () {
  43140. var legend = this.legend, navigator = this.navigator, scrollbarHeight, legendOptions, xAxis, yAxis;
  43141. if (navigator) {
  43142. legendOptions = legend && legend.options;
  43143. xAxis = navigator.xAxis;
  43144. yAxis = navigator.yAxis;
  43145. scrollbarHeight = navigator.scrollbarHeight;
  43146. // Compute the top position
  43147. if (this.inverted) {
  43148. navigator.left = navigator.opposite ?
  43149. this.chartWidth - scrollbarHeight -
  43150. navigator.height :
  43151. this.spacing[3] + scrollbarHeight;
  43152. navigator.top = this.plotTop + scrollbarHeight;
  43153. }
  43154. else {
  43155. navigator.left = this.plotLeft + scrollbarHeight;
  43156. navigator.top = navigator.navigatorOptions.top ||
  43157. this.chartHeight -
  43158. navigator.height -
  43159. scrollbarHeight -
  43160. this.spacing[2] -
  43161. (this.rangeSelector && this.extraBottomMargin ?
  43162. this.rangeSelector.getHeight() :
  43163. 0) -
  43164. ((legendOptions &&
  43165. legendOptions.verticalAlign === 'bottom' &&
  43166. legendOptions.enabled &&
  43167. !legendOptions.floating) ?
  43168. legend.legendHeight +
  43169. pick(legendOptions.margin, 10) :
  43170. 0) -
  43171. (this.titleOffset ? this.titleOffset[2] : 0);
  43172. }
  43173. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  43174. if (this.inverted) {
  43175. xAxis.options.left = yAxis.options.left = navigator.left;
  43176. }
  43177. else {
  43178. xAxis.options.top = yAxis.options.top = navigator.top;
  43179. }
  43180. xAxis.setAxisSize();
  43181. yAxis.setAxisSize();
  43182. }
  43183. }
  43184. });
  43185. // Merge options, if no scrolling exists yet
  43186. addEvent(Chart, 'update', function (e) {
  43187. var navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});
  43188. if (!this.navigator && !this.scroller &&
  43189. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  43190. merge(true, this.options.navigator, navigatorOptions);
  43191. merge(true, this.options.scrollbar, scrollbarOptions);
  43192. delete e.options.navigator;
  43193. delete e.options.scrollbar;
  43194. }
  43195. });
  43196. // Initialize navigator, if no scrolling exists yet
  43197. addEvent(Chart, 'afterUpdate', function (event) {
  43198. if (!this.navigator && !this.scroller &&
  43199. (this.options.navigator.enabled ||
  43200. this.options.scrollbar.enabled)) {
  43201. this.scroller = this.navigator = new Navigator(this);
  43202. if (pick(event.redraw, true)) {
  43203. this.redraw(event.animation); // #7067
  43204. }
  43205. }
  43206. });
  43207. // Handle adding new series
  43208. addEvent(Chart, 'afterAddSeries', function () {
  43209. if (this.navigator) {
  43210. // Recompute which series should be shown in navigator, and add them
  43211. this.navigator.setBaseSeries(null, false);
  43212. }
  43213. });
  43214. // Handle updating series
  43215. addEvent(Series, 'afterUpdate', function () {
  43216. if (this.chart.navigator && !this.options.isInternal) {
  43217. this.chart.navigator.setBaseSeries(null, false);
  43218. }
  43219. });
  43220. Chart.prototype.callbacks.push(function (chart) {
  43221. var extremes, navigator = chart.navigator;
  43222. // Initialize the navigator
  43223. if (navigator && chart.xAxis[0]) {
  43224. extremes = chart.xAxis[0].getExtremes();
  43225. navigator.render(extremes.min, extremes.max);
  43226. }
  43227. });
  43228. }
  43229. });
  43230. _registerModule(_modules, 'parts/OrdinalAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  43231. /* *
  43232. *
  43233. * (c) 2010-2019 Torstein Honsi
  43234. *
  43235. * License: www.highcharts.com/license
  43236. *
  43237. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43238. *
  43239. * */
  43240. var defined = U.defined, extend = U.extend, pick = U.pick;
  43241. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  43242. var addEvent = H.addEvent, Axis = H.Axis, Chart = H.Chart, css = H.css, noop = H.noop, Series = H.Series, timeUnits = H.timeUnits;
  43243. /* eslint-disable no-invalid-this, valid-jsdoc */
  43244. /* ************************************************************************** *
  43245. * Start ordinal axis logic *
  43246. * ************************************************************************** */
  43247. addEvent(Series, 'updatedData', function () {
  43248. var xAxis = this.xAxis;
  43249. // Destroy the extended ordinal index on updated data
  43250. if (xAxis && xAxis.options.ordinal) {
  43251. delete xAxis.ordinalIndex;
  43252. }
  43253. });
  43254. /**
  43255. * In an ordinal axis, there might be areas with dense consentrations of points,
  43256. * then large gaps between some. Creating equally distributed ticks over this
  43257. * entire range may lead to a huge number of ticks that will later be removed.
  43258. * So instead, break the positions up in segments, find the tick positions for
  43259. * each segment then concatenize them. This method is used from both data
  43260. * grouping logic and X axis tick position logic.
  43261. * @private
  43262. */
  43263. Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  43264. var start = 0, end, segmentPositions, higherRanks = {}, hasCrossedHigherRank, info, posLength, outsideMax, groupPositions = [], lastGroupPosition = -Number.MAX_VALUE, tickPixelIntervalOption = this.options.tickPixelInterval, time = this.chart.time,
  43265. // Record all the start positions of a segment, to use when deciding
  43266. // what's a gap in the data.
  43267. segmentStarts = [];
  43268. // The positions are not always defined, for example for ordinal positions
  43269. // when data has regular interval (#1557, #2090)
  43270. if ((!this.options.ordinal && !this.options.breaks) ||
  43271. !positions ||
  43272. positions.length < 3 ||
  43273. typeof min === 'undefined') {
  43274. return time.getTimeTicks.apply(time, arguments);
  43275. }
  43276. // Analyze the positions array to split it into segments on gaps larger than
  43277. // 5 times the closest distance. The closest distance is already found at
  43278. // this point, so we reuse that instead of computing it again.
  43279. posLength = positions.length;
  43280. for (end = 0; end < posLength; end++) {
  43281. outsideMax = end && positions[end - 1] > max;
  43282. if (positions[end] < min) { // Set the last position before min
  43283. start = end;
  43284. }
  43285. if (end === posLength - 1 ||
  43286. positions[end + 1] - positions[end] > closestDistance * 5 ||
  43287. outsideMax) {
  43288. // For each segment, calculate the tick positions from the
  43289. // getTimeTicks utility function. The interval will be the same
  43290. // regardless of how long the segment is.
  43291. if (positions[end] > lastGroupPosition) { // #1475
  43292. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  43293. // Prevent duplicate groups, for example for multiple segments
  43294. // within one larger time frame (#1475)
  43295. while (segmentPositions.length &&
  43296. segmentPositions[0] <= lastGroupPosition) {
  43297. segmentPositions.shift();
  43298. }
  43299. if (segmentPositions.length) {
  43300. lastGroupPosition =
  43301. segmentPositions[segmentPositions.length - 1];
  43302. }
  43303. segmentStarts.push(groupPositions.length);
  43304. groupPositions = groupPositions.concat(segmentPositions);
  43305. }
  43306. // Set start of next segment
  43307. start = end + 1;
  43308. }
  43309. if (outsideMax) {
  43310. break;
  43311. }
  43312. }
  43313. // Get the grouping info from the last of the segments. The info is the same
  43314. // for all segments.
  43315. info = segmentPositions.info;
  43316. // Optionally identify ticks with higher rank, for example when the ticks
  43317. // have crossed midnight.
  43318. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  43319. end = groupPositions.length - 1;
  43320. // Compare points two by two
  43321. for (start = 1; start < end; start++) {
  43322. if (time.dateFormat('%d', groupPositions[start]) !==
  43323. time.dateFormat('%d', groupPositions[start - 1])) {
  43324. higherRanks[groupPositions[start]] = 'day';
  43325. hasCrossedHigherRank = true;
  43326. }
  43327. }
  43328. // If the complete array has crossed midnight, we want to mark the first
  43329. // positions also as higher rank
  43330. if (hasCrossedHigherRank) {
  43331. higherRanks[groupPositions[0]] = 'day';
  43332. }
  43333. info.higherRanks = higherRanks;
  43334. }
  43335. // Save the info
  43336. info.segmentStarts = segmentStarts;
  43337. groupPositions.info = info;
  43338. // Don't show ticks within a gap in the ordinal axis, where the space
  43339. // between two points is greater than a portion of the tick pixel interval
  43340. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  43341. var length = groupPositions.length, i = length, itemToRemove, translated, translatedArr = [], lastTranslated, medianDistance, distance, distances = [];
  43342. // Find median pixel distance in order to keep a reasonably even
  43343. // distance between ticks (#748)
  43344. while (i--) {
  43345. translated = this.translate(groupPositions[i]);
  43346. if (lastTranslated) {
  43347. distances[i] = lastTranslated - translated;
  43348. }
  43349. translatedArr[i] = lastTranslated = translated;
  43350. }
  43351. distances.sort();
  43352. medianDistance = distances[Math.floor(distances.length / 2)];
  43353. if (medianDistance < tickPixelIntervalOption * 0.6) {
  43354. medianDistance = null;
  43355. }
  43356. // Now loop over again and remove ticks where needed
  43357. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  43358. lastTranslated = void 0;
  43359. while (i--) {
  43360. translated = translatedArr[i];
  43361. distance = Math.abs(lastTranslated - translated);
  43362. // #4175 - when axis is reversed, the distance, is negative but
  43363. // tickPixelIntervalOption positive, so we need to compare the same
  43364. // values
  43365. // Remove ticks that are closer than 0.6 times the pixel interval
  43366. // from the one to the right, but not if it is close to the median
  43367. // distance (#748).
  43368. if (lastTranslated &&
  43369. distance < tickPixelIntervalOption * 0.8 &&
  43370. (medianDistance === null || distance < medianDistance * 0.8)) {
  43371. // Is this a higher ranked position with a normal position to
  43372. // the right?
  43373. if (higherRanks[groupPositions[i]] &&
  43374. !higherRanks[groupPositions[i + 1]]) {
  43375. // Yes: remove the lower ranked neighbour to the right
  43376. itemToRemove = i + 1;
  43377. lastTranslated = translated; // #709
  43378. }
  43379. else {
  43380. // No: remove this one
  43381. itemToRemove = i;
  43382. }
  43383. groupPositions.splice(itemToRemove, 1);
  43384. }
  43385. else {
  43386. lastTranslated = translated;
  43387. }
  43388. }
  43389. }
  43390. return groupPositions;
  43391. };
  43392. // Extend the Axis prototype
  43393. extend(Axis.prototype, /** @lends Axis.prototype */ {
  43394. /**
  43395. * Calculate the ordinal positions before tick positions are calculated.
  43396. *
  43397. * @private
  43398. * @function Highcharts.Axis#beforeSetTickPositions
  43399. * @return {void}
  43400. */
  43401. beforeSetTickPositions: function () {
  43402. var axis = this, 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;
  43403. // Apply the ordinal logic
  43404. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  43405. axis.series.forEach(function (series, i) {
  43406. uniqueOrdinalPositions = [];
  43407. if ((!ignoreHiddenSeries || series.visible !== false) &&
  43408. (series.takeOrdinalPosition !== false || hasBreaks)) {
  43409. // concatenate the processed X data into the existing
  43410. // positions, or the empty array
  43411. ordinalPositions = ordinalPositions.concat(series.processedXData);
  43412. len = ordinalPositions.length;
  43413. // remove duplicates (#1588)
  43414. ordinalPositions.sort(function (a, b) {
  43415. // without a custom function it is sorted as strings
  43416. return a - b;
  43417. });
  43418. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  43419. // Check for a single-point series:
  43420. series.closestPointRange, overscrollPointsRange));
  43421. if (len) {
  43422. i = 0;
  43423. while (i < len - 1) {
  43424. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  43425. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  43426. }
  43427. i++;
  43428. }
  43429. // Check first item:
  43430. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  43431. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  43432. }
  43433. ordinalPositions = uniqueOrdinalPositions;
  43434. }
  43435. }
  43436. if (series.isSeriesBoosting) {
  43437. hasBoostedSeries = true;
  43438. }
  43439. });
  43440. if (hasBoostedSeries) {
  43441. ordinalPositions.length = 0;
  43442. }
  43443. // cache the length
  43444. len = ordinalPositions.length;
  43445. // Check if we really need the overhead of mapping axis data against
  43446. // the ordinal positions. If the series consist of evenly spaced
  43447. // data any way, we don't need any ordinal logic.
  43448. if (len > 2) { // two points have equal distance by default
  43449. dist = ordinalPositions[1] - ordinalPositions[0];
  43450. i = len - 1;
  43451. while (i-- && !useOrdinal) {
  43452. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  43453. useOrdinal = true;
  43454. }
  43455. }
  43456. // When zooming in on a week, prevent axis padding for weekends
  43457. // even though the data within the week is evenly spaced.
  43458. if (!axis.options.keepOrdinalPadding &&
  43459. (ordinalPositions[0] - min > dist ||
  43460. max - ordinalPositions[ordinalPositions.length - 1] >
  43461. dist)) {
  43462. useOrdinal = true;
  43463. }
  43464. }
  43465. else if (axis.options.overscroll) {
  43466. if (len === 2) {
  43467. // Exactly two points, distance for overscroll is fixed:
  43468. overscrollPointsRange =
  43469. ordinalPositions[1] - ordinalPositions[0];
  43470. }
  43471. else if (len === 1) {
  43472. // We have just one point, closest distance is unknown.
  43473. // Assume then it is last point and overscrolled range:
  43474. overscrollPointsRange = axis.options.overscroll;
  43475. ordinalPositions = [
  43476. ordinalPositions[0],
  43477. ordinalPositions[0] + overscrollPointsRange
  43478. ];
  43479. }
  43480. else {
  43481. // In case of zooming in on overscrolled range, stick to the
  43482. // old range:
  43483. overscrollPointsRange = axis.overscrollPointsRange;
  43484. }
  43485. }
  43486. // Record the slope and offset to compute the linear values from the
  43487. // array index. Since the ordinal positions may exceed the current
  43488. // range, get the start and end positions within it (#719, #665b)
  43489. if (useOrdinal) {
  43490. if (axis.options.overscroll) {
  43491. axis.overscrollPointsRange = overscrollPointsRange;
  43492. ordinalPositions = ordinalPositions.concat(axis.getOverscrollPositions());
  43493. }
  43494. // Register
  43495. axis.ordinalPositions = ordinalPositions;
  43496. // This relies on the ordinalPositions being set. Use Math.max
  43497. // and Math.min to prevent padding on either sides of the data.
  43498. minIndex = axis.ordinal2lin(// #5979
  43499. Math.max(min, ordinalPositions[0]), true);
  43500. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  43501. // Set the slope and offset of the values compared to the
  43502. // indices in the ordinal positions
  43503. axis.ordinalSlope = slope = (max - min) / (maxIndex - minIndex);
  43504. axis.ordinalOffset = min - (minIndex * slope);
  43505. }
  43506. else {
  43507. axis.overscrollPointsRange = pick(axis.closestPointRange, axis.overscrollPointsRange);
  43508. axis.ordinalPositions = axis.ordinalSlope = axis.ordinalOffset =
  43509. void 0;
  43510. }
  43511. }
  43512. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  43513. axis.groupIntervalFactor = null; // reset for next run
  43514. },
  43515. /**
  43516. * Translate from a linear axis value to the corresponding ordinal axis
  43517. * position. If there are no gaps in the ordinal axis this will be the same.
  43518. * The translated value is the value that the point would have if the axis
  43519. * were linear, using the same min and max.
  43520. *
  43521. * @private
  43522. * @function Highcharts.Axis#val2lin
  43523. *
  43524. * @param {number} val
  43525. * The axis value.
  43526. *
  43527. * @param {boolean} [toIndex]
  43528. * Whether to return the index in the ordinalPositions or the new
  43529. * value.
  43530. *
  43531. * @return {number}
  43532. */
  43533. val2lin: function (val, toIndex) {
  43534. var axis = this, ordinalPositions = axis.ordinalPositions, ret;
  43535. if (!ordinalPositions) {
  43536. ret = val;
  43537. }
  43538. else {
  43539. var ordinalLength = ordinalPositions.length, i, distance, ordinalIndex;
  43540. // first look for an exact match in the ordinalpositions array
  43541. i = ordinalLength;
  43542. while (i--) {
  43543. if (ordinalPositions[i] === val) {
  43544. ordinalIndex = i;
  43545. break;
  43546. }
  43547. }
  43548. // if that failed, find the intermediate position between the two
  43549. // nearest values
  43550. i = ordinalLength - 1;
  43551. while (i--) {
  43552. if (val > ordinalPositions[i] || i === 0) { // interpolate
  43553. // something between 0 and 1
  43554. distance = (val - ordinalPositions[i]) /
  43555. (ordinalPositions[i + 1] - ordinalPositions[i]);
  43556. ordinalIndex = i + distance;
  43557. break;
  43558. }
  43559. }
  43560. ret = toIndex ?
  43561. ordinalIndex :
  43562. axis.ordinalSlope *
  43563. (ordinalIndex || 0) +
  43564. axis.ordinalOffset;
  43565. }
  43566. return ret;
  43567. },
  43568. /**
  43569. * Translate from linear (internal) to axis value.
  43570. *
  43571. * @private
  43572. * @function Highcharts.Axis#lin2val
  43573. *
  43574. * @param {number} val
  43575. * The linear abstracted value.
  43576. *
  43577. * @param {boolean} [fromIndex]
  43578. * Translate from an index in the ordinal positions rather than a
  43579. * value.
  43580. *
  43581. * @return {number}
  43582. */
  43583. lin2val: function (val, fromIndex) {
  43584. var axis = this, ordinalPositions = axis.ordinalPositions, ret;
  43585. // the visible range contains only equally spaced values
  43586. if (!ordinalPositions) {
  43587. ret = val;
  43588. }
  43589. else {
  43590. var ordinalSlope = axis.ordinalSlope, ordinalOffset = axis.ordinalOffset, i = ordinalPositions.length - 1, linearEquivalentLeft, linearEquivalentRight, distance;
  43591. // Handle the case where we translate from the index directly, used
  43592. // only when panning an ordinal axis
  43593. if (fromIndex) {
  43594. if (val < 0) { // out of range, in effect panning to the left
  43595. val = ordinalPositions[0];
  43596. }
  43597. else if (val > i) { // out of range, panning to the right
  43598. val = ordinalPositions[i];
  43599. }
  43600. else { // split it up
  43601. i = Math.floor(val);
  43602. distance = val - i; // the decimal
  43603. }
  43604. // Loop down along the ordinal positions. When the linear equivalent
  43605. // of i matches an ordinal position, interpolate between the left
  43606. // and right values.
  43607. }
  43608. else {
  43609. while (i--) {
  43610. linearEquivalentLeft =
  43611. (ordinalSlope * i) + ordinalOffset;
  43612. if (val >= linearEquivalentLeft) {
  43613. linearEquivalentRight =
  43614. (ordinalSlope *
  43615. (i + 1)) +
  43616. ordinalOffset;
  43617. // something between 0 and 1
  43618. distance = (val - linearEquivalentLeft) /
  43619. (linearEquivalentRight - linearEquivalentLeft);
  43620. break;
  43621. }
  43622. }
  43623. }
  43624. // If the index is within the range of the ordinal positions, return
  43625. // the associated or interpolated value. If not, just return the
  43626. // value
  43627. return (typeof distance !== 'undefined' &&
  43628. typeof ordinalPositions[i] !== 'undefined' ?
  43629. ordinalPositions[i] + (distance ?
  43630. distance *
  43631. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  43632. 0) :
  43633. val);
  43634. }
  43635. return ret;
  43636. },
  43637. /**
  43638. * Get the ordinal positions for the entire data set. This is necessary in
  43639. * chart panning because we need to find out what points or data groups are
  43640. * available outside the visible range. When a panning operation starts, if
  43641. * an index for the given grouping does not exists, it is created and
  43642. * cached. This index is deleted on updated data, so it will be regenerated
  43643. * the next time a panning operation starts.
  43644. *
  43645. * @private
  43646. * @function Highcharts.Axis#getExtendedPositions
  43647. *
  43648. * @return {Array<number>}
  43649. */
  43650. getExtendedPositions: function () {
  43651. var axis = this, chart = axis.chart, grouping = axis.series[0].currentDataGrouping, ordinalIndex = axis.ordinalIndex, key = grouping ?
  43652. grouping.count + grouping.unitName :
  43653. 'raw', overscroll = axis.options.overscroll, extremes = axis.getExtremes(), fakeAxis, fakeSeries;
  43654. // If this is the first time, or the ordinal index is deleted by
  43655. // updatedData,
  43656. // create it.
  43657. if (!ordinalIndex) {
  43658. ordinalIndex = axis.ordinalIndex = {};
  43659. }
  43660. if (!ordinalIndex[key]) {
  43661. // Create a fake axis object where the extended ordinal positions
  43662. // are emulated
  43663. fakeAxis = {
  43664. series: [],
  43665. chart: chart,
  43666. getExtremes: function () {
  43667. return {
  43668. min: extremes.dataMin,
  43669. max: extremes.dataMax + overscroll
  43670. };
  43671. },
  43672. options: {
  43673. ordinal: true
  43674. },
  43675. val2lin: Axis.prototype.val2lin,
  43676. ordinal2lin: Axis.prototype.ordinal2lin // #6276
  43677. };
  43678. // Add the fake series to hold the full data, then apply processData
  43679. // to it
  43680. axis.series.forEach(function (series) {
  43681. fakeSeries = {
  43682. xAxis: fakeAxis,
  43683. xData: series.xData.slice(),
  43684. chart: chart,
  43685. destroyGroupedData: noop
  43686. };
  43687. fakeSeries.xData = fakeSeries.xData.concat(axis.getOverscrollPositions());
  43688. fakeSeries.options = {
  43689. dataGrouping: grouping ? {
  43690. enabled: true,
  43691. forced: true,
  43692. // doesn't matter which, use the fastest
  43693. approximation: 'open',
  43694. units: [[
  43695. grouping.unitName,
  43696. [grouping.count]
  43697. ]]
  43698. } : {
  43699. enabled: false
  43700. }
  43701. };
  43702. series.processData.apply(fakeSeries);
  43703. fakeAxis.series.push(fakeSeries);
  43704. });
  43705. // Run beforeSetTickPositions to compute the ordinalPositions
  43706. axis.beforeSetTickPositions.apply(fakeAxis);
  43707. // Cache it
  43708. ordinalIndex[key] = fakeAxis.ordinalPositions;
  43709. }
  43710. return ordinalIndex[key];
  43711. },
  43712. /**
  43713. * Get ticks for an ordinal axis within a range where points don't exist.
  43714. * It is required when overscroll is enabled. We can't base on points,
  43715. * because we may not have any, so we use approximated pointRange and
  43716. * generate these ticks between Axis.dataMax, Axis.dataMax + Axis.overscroll
  43717. * evenly spaced. Used in panning and navigator scrolling.
  43718. *
  43719. * @private
  43720. * @function Highcharts.Axis#getOverscrollPositions
  43721. *
  43722. * @returns {Array<number>}
  43723. * Generated ticks
  43724. */
  43725. getOverscrollPositions: function () {
  43726. var axis = this, extraRange = axis.options.overscroll, distance = axis.overscrollPointsRange, positions = [], max = axis.dataMax;
  43727. if (defined(distance)) {
  43728. // Max + pointRange because we need to scroll to the last
  43729. positions.push(max);
  43730. while (max <= axis.dataMax + extraRange) {
  43731. max += distance;
  43732. positions.push(max);
  43733. }
  43734. }
  43735. return positions;
  43736. },
  43737. /**
  43738. * Find the factor to estimate how wide the plot area would have been if
  43739. * ordinal gaps were included. This value is used to compute an imagined
  43740. * plot width in order to establish the data grouping interval.
  43741. *
  43742. * A real world case is the intraday-candlestick example. Without this
  43743. * logic, it would show the correct data grouping when viewing a range
  43744. * within each day, but once moving the range to include the gap between two
  43745. * days, the interval would include the cut-away night hours and the data
  43746. * grouping would be wrong. So the below method tries to compensate by
  43747. * identifying the most common point interval, in this case days.
  43748. *
  43749. * An opposite case is presented in issue #718. We have a long array of
  43750. * daily data, then one point is appended one hour after the last point. We
  43751. * expect the data grouping not to change.
  43752. *
  43753. * In the future, if we find cases where this estimation doesn't work
  43754. * optimally, we might need to add a second pass to the data grouping logic,
  43755. * where we do another run with a greater interval if the number of data
  43756. * groups is more than a certain fraction of the desired group count.
  43757. *
  43758. * @private
  43759. * @function Highcharts.Axis#getGroupIntervalFactor
  43760. *
  43761. * @param {number} xMin
  43762. *
  43763. * @param {number} xMax
  43764. *
  43765. * @param {Highcharts.Series} series
  43766. *
  43767. * @return {number}
  43768. */
  43769. getGroupIntervalFactor: function (xMin, xMax, series) {
  43770. var i, processedXData = series.processedXData, len = processedXData.length, distances = [], median, groupIntervalFactor = this.groupIntervalFactor;
  43771. // Only do this computation for the first series, let the other inherit
  43772. // it (#2416)
  43773. if (!groupIntervalFactor) {
  43774. // Register all the distances in an array
  43775. for (i = 0; i < len - 1; i++) {
  43776. distances[i] =
  43777. processedXData[i + 1] - processedXData[i];
  43778. }
  43779. // Sort them and find the median
  43780. distances.sort(function (a, b) {
  43781. return a - b;
  43782. });
  43783. median = distances[Math.floor(len / 2)];
  43784. // Compensate for series that don't extend through the entire axis
  43785. // extent. #1675.
  43786. xMin = Math.max(xMin, processedXData[0]);
  43787. xMax = Math.min(xMax, processedXData[len - 1]);
  43788. this.groupIntervalFactor = groupIntervalFactor =
  43789. (len * median) / (xMax - xMin);
  43790. }
  43791. // Return the factor needed for data grouping
  43792. return groupIntervalFactor;
  43793. },
  43794. /**
  43795. * Make the tick intervals closer because the ordinal gaps make the ticks
  43796. * spread out or cluster.
  43797. *
  43798. * @private
  43799. * @function Highcharts.Axis#postProcessTickInterval
  43800. *
  43801. * @param {number} tickInterval
  43802. *
  43803. * @return {number}
  43804. */
  43805. postProcessTickInterval: function (tickInterval) {
  43806. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  43807. // This is a case where this algorithm doesn't work optimally. In this
  43808. // case, the tick labels are spread out per week, but all the gaps
  43809. // reside within weeks. So we have a situation where the labels are
  43810. // courser than the ordinal gaps, and thus the tick interval should not
  43811. // be altered
  43812. var ordinalSlope = this.ordinalSlope, ret;
  43813. if (ordinalSlope) {
  43814. if (!this.options.breaks) {
  43815. ret = tickInterval / (ordinalSlope / this.closestPointRange);
  43816. }
  43817. else {
  43818. ret = this.closestPointRange || tickInterval; // #7275
  43819. }
  43820. }
  43821. else {
  43822. ret = tickInterval;
  43823. }
  43824. return ret;
  43825. }
  43826. });
  43827. // Record this to prevent overwriting by broken-axis module (#5979)
  43828. Axis.prototype.ordinal2lin = Axis.prototype.val2lin;
  43829. // Extending the Chart.pan method for ordinal axes
  43830. addEvent(Chart, 'pan', function (e) {
  43831. var chart = this, xAxis = chart.xAxis[0], overscroll = xAxis.options.overscroll, chartX = e.originalEvent.chartX, panning = chart.options.chart &&
  43832. chart.options.chart.panning, runBase = false;
  43833. if (panning &&
  43834. panning.type !== 'y' &&
  43835. xAxis.options.ordinal &&
  43836. xAxis.series.length) {
  43837. var mouseDownX = chart.mouseDownX, extremes = xAxis.getExtremes(), dataMax = extremes.dataMax, min = extremes.min, max = extremes.max, trimmedRange, hoverPoints = chart.hoverPoints, closestPointRange = xAxis.closestPointRange || xAxis.overscrollPointsRange, pointPixelWidth = (xAxis.translationSlope *
  43838. (xAxis.ordinalSlope || closestPointRange)),
  43839. // how many ordinal units did we move?
  43840. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  43841. // get index of all the chart's points
  43842. extendedAxis = { ordinalPositions: xAxis.getExtendedPositions() }, ordinalPositions, searchAxisLeft, lin2val = xAxis.lin2val, val2lin = xAxis.val2lin, searchAxisRight;
  43843. // we have an ordinal axis, but the data is equally spaced
  43844. if (!extendedAxis.ordinalPositions) {
  43845. runBase = true;
  43846. }
  43847. else if (Math.abs(movedUnits) > 1) {
  43848. // Remove active points for shared tooltip
  43849. if (hoverPoints) {
  43850. hoverPoints.forEach(function (point) {
  43851. point.setState();
  43852. });
  43853. }
  43854. if (movedUnits < 0) {
  43855. searchAxisLeft = extendedAxis;
  43856. searchAxisRight = xAxis.ordinalPositions ? xAxis : extendedAxis;
  43857. }
  43858. else {
  43859. searchAxisLeft = xAxis.ordinalPositions ? xAxis : extendedAxis;
  43860. searchAxisRight = extendedAxis;
  43861. }
  43862. // In grouped data series, the last ordinal position represents the
  43863. // grouped data, which is to the left of the real data max. If we
  43864. // don't compensate for this, we will be allowed to pan grouped data
  43865. // series passed the right of the plot area.
  43866. ordinalPositions = searchAxisRight.ordinalPositions;
  43867. if (dataMax >
  43868. ordinalPositions[ordinalPositions.length - 1]) {
  43869. ordinalPositions.push(dataMax);
  43870. }
  43871. // Get the new min and max values by getting the ordinal index for
  43872. // the current extreme, then add the moved units and translate back
  43873. // to values. This happens on the extended ordinal positions if the
  43874. // new position is out of range, else it happens on the current x
  43875. // axis which is smaller and faster.
  43876. chart.fixedRange = max - min;
  43877. trimmedRange = xAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  43878. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  43879. true // translate from index
  43880. ]), lin2val.apply(searchAxisRight, [
  43881. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  43882. true // translate from index
  43883. ]));
  43884. // Apply it if it is within the available data range
  43885. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  43886. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  43887. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  43888. }
  43889. chart.mouseDownX = chartX; // set new reference for next run
  43890. css(chart.container, { cursor: 'move' });
  43891. }
  43892. }
  43893. else {
  43894. runBase = true;
  43895. }
  43896. // revert to the linear chart.pan version
  43897. if (runBase || (panning && /y/.test(panning.type))) {
  43898. if (overscroll) {
  43899. xAxis.max = xAxis.dataMax + overscroll;
  43900. }
  43901. }
  43902. else {
  43903. e.preventDefault();
  43904. }
  43905. });
  43906. addEvent(Axis, 'foundExtremes', function () {
  43907. var axis = this;
  43908. if (axis.isXAxis &&
  43909. defined(axis.options.overscroll) &&
  43910. axis.max === axis.dataMax &&
  43911. (
  43912. // Panning is an execption,
  43913. // We don't want to apply overscroll when panning over the dataMax
  43914. !axis.chart.mouseIsDown ||
  43915. axis.isInternal) && (
  43916. // Scrollbar buttons are the other execption:
  43917. !axis.eventArgs ||
  43918. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  43919. axis.max += axis.options.overscroll;
  43920. // Live data and buttons require translation for the min:
  43921. if (!axis.isInternal && defined(axis.userMin)) {
  43922. axis.min += axis.options.overscroll;
  43923. }
  43924. }
  43925. });
  43926. // For ordinal axis, that loads data async, redraw axis after data is loaded.
  43927. // If we don't do that, axis will have the same extremes as previously, but
  43928. // ordinal positions won't be calculated. See #10290
  43929. addEvent(Axis, 'afterSetScale', function () {
  43930. var axis = this;
  43931. if (axis.horiz && !axis.isDirty) {
  43932. axis.isDirty = axis.isOrdinal &&
  43933. axis.chart.navigator &&
  43934. !axis.chart.navigator.adaptToUpdatedData;
  43935. }
  43936. });
  43937. });
  43938. _registerModule(_modules, 'modules/broken-axis.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  43939. /* *
  43940. *
  43941. * (c) 2009-2019 Torstein Honsi
  43942. *
  43943. * License: www.highcharts.com/license
  43944. *
  43945. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43946. *
  43947. * */
  43948. var extend = U.extend, isArray = U.isArray, pick = U.pick;
  43949. var addEvent = H.addEvent, find = H.find, fireEvent = H.fireEvent, Axis = H.Axis, Series = H.Series;
  43950. /**
  43951. * Returns the first break found where the x is larger then break.from and
  43952. * smaller then break.to.
  43953. *
  43954. * @param {number} x
  43955. * The number which should be within a break.
  43956. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  43957. * The array of breaks to search within.
  43958. * @return {Highcharts.XAxisBreaksOptions|undefined}
  43959. * Returns the first break found that matches, returns false if no break
  43960. * is found.
  43961. */
  43962. var findBreakAt = function (x, breaks) {
  43963. return find(breaks, function (b) {
  43964. return b.from < x && x < b.to;
  43965. });
  43966. };
  43967. extend(Axis.prototype, {
  43968. isInBreak: function (brk, val) {
  43969. var ret, repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  43970. (val - from) % repeat :
  43971. repeat - ((from - val) % repeat));
  43972. if (!brk.inclusive) {
  43973. ret = test < length && test !== 0;
  43974. }
  43975. else {
  43976. ret = test <= length;
  43977. }
  43978. return ret;
  43979. },
  43980. isInAnyBreak: function (val, testKeep) {
  43981. var breaks = this.options.breaks, i = breaks && breaks.length, inbrk, keep, ret;
  43982. if (i) {
  43983. while (i--) {
  43984. if (this.isInBreak(breaks[i], val)) {
  43985. inbrk = true;
  43986. if (!keep) {
  43987. keep = pick(breaks[i].showPoints, !this.isXAxis);
  43988. }
  43989. }
  43990. }
  43991. if (inbrk && testKeep) {
  43992. ret = inbrk && !keep;
  43993. }
  43994. else {
  43995. ret = inbrk;
  43996. }
  43997. }
  43998. return ret;
  43999. }
  44000. });
  44001. /* eslint-disable no-invalid-this */
  44002. addEvent(Axis, 'afterInit', function () {
  44003. if (typeof this.setBreaks === 'function') {
  44004. this.setBreaks(this.options.breaks, false);
  44005. }
  44006. });
  44007. addEvent(Axis, 'afterSetTickPositions', function () {
  44008. if (this.isBroken) {
  44009. var axis = this, tickPositions = this.tickPositions, info = this.tickPositions.info, newPositions = [], i;
  44010. for (i = 0; i < tickPositions.length; i++) {
  44011. if (!axis.isInAnyBreak(tickPositions[i])) {
  44012. newPositions.push(tickPositions[i]);
  44013. }
  44014. }
  44015. this.tickPositions = newPositions;
  44016. this.tickPositions.info = info;
  44017. }
  44018. });
  44019. // Force Axis to be not-ordinal when breaks are defined
  44020. addEvent(Axis, 'afterSetOptions', function () {
  44021. if (this.isBroken) {
  44022. this.options.ordinal = false;
  44023. }
  44024. });
  44025. /**
  44026. * Dynamically set or unset breaks in an axis. This function in lighter than
  44027. * usin Axis.update, and it also preserves animation.
  44028. *
  44029. * @private
  44030. * @function Highcharts.Axis#setBreaks
  44031. *
  44032. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  44033. * The breaks to add. When `undefined` it removes existing breaks.
  44034. *
  44035. * @param {boolean} [redraw=true]
  44036. * Whether to redraw the chart immediately.
  44037. *
  44038. * @return {void}
  44039. */
  44040. Axis.prototype.setBreaks = function (breaks, redraw) {
  44041. var axis = this, isBroken = (isArray(breaks) && !!breaks.length);
  44042. /* eslint-disable valid-jsdoc */
  44043. /**
  44044. * @private
  44045. */
  44046. function breakVal2Lin(val) {
  44047. var nval = val, brk, i;
  44048. for (i = 0; i < axis.breakArray.length; i++) {
  44049. brk = axis.breakArray[i];
  44050. if (brk.to <= val) {
  44051. nval -= brk.len;
  44052. }
  44053. else if (brk.from >= val) {
  44054. break;
  44055. }
  44056. else if (axis.isInBreak(brk, val)) {
  44057. nval -= (val - brk.from);
  44058. break;
  44059. }
  44060. }
  44061. return nval;
  44062. }
  44063. /**
  44064. * @private
  44065. */
  44066. function breakLin2Val(val) {
  44067. var nval = val, brk, i;
  44068. for (i = 0; i < axis.breakArray.length; i++) {
  44069. brk = axis.breakArray[i];
  44070. if (brk.from >= nval) {
  44071. break;
  44072. }
  44073. else if (brk.to < nval) {
  44074. nval += brk.len;
  44075. }
  44076. else if (axis.isInBreak(brk, nval)) {
  44077. nval += brk.len;
  44078. }
  44079. }
  44080. return nval;
  44081. }
  44082. /* eslint-enable valid-jsdoc */
  44083. axis.isDirty = axis.isBroken !== isBroken;
  44084. axis.isBroken = isBroken;
  44085. axis.options.breaks = axis.userOptions.breaks = breaks;
  44086. axis.forceRedraw = true; // Force recalculation in setScale
  44087. // Recalculate series related to the axis.
  44088. axis.series.forEach(function (series) {
  44089. series.isDirty = true;
  44090. });
  44091. if (!isBroken && axis.val2lin === breakVal2Lin) {
  44092. // Revert to prototype functions
  44093. delete axis.val2lin;
  44094. delete axis.lin2val;
  44095. }
  44096. if (isBroken) {
  44097. axis.userOptions.ordinal = false;
  44098. axis.val2lin = breakVal2Lin;
  44099. axis.lin2val = breakLin2Val;
  44100. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  44101. // If trying to set extremes inside a break, extend min to after,
  44102. // and max to before the break ( #3857 )
  44103. if (this.isBroken) {
  44104. var axisBreak, breaks = this.options.breaks;
  44105. while ((axisBreak = findBreakAt(newMin, breaks))) {
  44106. newMin = axisBreak.to;
  44107. }
  44108. while ((axisBreak = findBreakAt(newMax, breaks))) {
  44109. newMax = axisBreak.from;
  44110. }
  44111. // If both min and max is within the same break.
  44112. if (newMax < newMin) {
  44113. newMax = newMin;
  44114. }
  44115. }
  44116. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  44117. };
  44118. axis.setAxisTranslation = function (saveOld) {
  44119. Axis.prototype.setAxisTranslation.call(this, saveOld);
  44120. this.unitLength = null;
  44121. if (this.isBroken) {
  44122. var breaks = axis.options.breaks,
  44123. // Temporary one:
  44124. breakArrayT = [], breakArray = [], length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, pointRangePadding = pick(axis.pointRangePadding, 0), start, i;
  44125. // Min & max check (#4247)
  44126. breaks.forEach(function (brk) {
  44127. repeat = brk.repeat || Infinity;
  44128. if (axis.isInBreak(brk, min)) {
  44129. min +=
  44130. (brk.to % repeat) -
  44131. (min % repeat);
  44132. }
  44133. if (axis.isInBreak(brk, max)) {
  44134. max -=
  44135. (max % repeat) -
  44136. (brk.from % repeat);
  44137. }
  44138. });
  44139. // Construct an array holding all breaks in the axis
  44140. breaks.forEach(function (brk) {
  44141. start = brk.from;
  44142. repeat = brk.repeat || Infinity;
  44143. while (start - repeat > min) {
  44144. start -= repeat;
  44145. }
  44146. while (start < min) {
  44147. start += repeat;
  44148. }
  44149. for (i = start; i < max; i += repeat) {
  44150. breakArrayT.push({
  44151. value: i,
  44152. move: 'in'
  44153. });
  44154. breakArrayT.push({
  44155. value: i + (brk.to - brk.from),
  44156. move: 'out',
  44157. size: brk.breakSize
  44158. });
  44159. }
  44160. });
  44161. breakArrayT.sort(function (a, b) {
  44162. return ((a.value === b.value) ?
  44163. ((a.move === 'in' ? 0 : 1) -
  44164. (b.move === 'in' ? 0 : 1)) :
  44165. a.value - b.value);
  44166. });
  44167. // Simplify the breaks
  44168. inBrk = 0;
  44169. start = min;
  44170. breakArrayT.forEach(function (brk) {
  44171. inBrk += (brk.move === 'in' ? 1 : -1);
  44172. if (inBrk === 1 && brk.move === 'in') {
  44173. start = brk.value;
  44174. }
  44175. if (inBrk === 0) {
  44176. breakArray.push({
  44177. from: start,
  44178. to: brk.value,
  44179. len: brk.value - start - (brk.size || 0)
  44180. });
  44181. length += brk.value - start - (brk.size || 0);
  44182. }
  44183. });
  44184. axis.breakArray = breakArray;
  44185. // Used with staticScale, and below, the actual axis length when
  44186. // breaks are substracted.
  44187. axis.unitLength =
  44188. max - min - length + pointRangePadding;
  44189. fireEvent(axis, 'afterBreaks');
  44190. if (axis.staticScale) {
  44191. axis.transA = axis.staticScale;
  44192. }
  44193. else if (axis.unitLength) {
  44194. axis.transA *=
  44195. (max - axis.min + pointRangePadding) /
  44196. axis.unitLength;
  44197. }
  44198. if (pointRangePadding) {
  44199. axis.minPixelPadding =
  44200. axis.transA * axis.minPointOffset;
  44201. }
  44202. axis.min = min;
  44203. axis.max = max;
  44204. }
  44205. };
  44206. }
  44207. if (pick(redraw, true)) {
  44208. this.chart.redraw();
  44209. }
  44210. };
  44211. addEvent(Series, 'afterGeneratePoints', function () {
  44212. var _a = this, isDirty = _a.isDirty, connectNulls = _a.options.connectNulls, points = _a.points, xAxis = _a.xAxis, yAxis = _a.yAxis;
  44213. /* Set, or reset visibility of the points. Axis.setBreaks marks the series
  44214. as isDirty */
  44215. if (isDirty) {
  44216. var i = points.length;
  44217. while (i--) {
  44218. var point = points[i];
  44219. // Respect nulls inside the break (#4275)
  44220. var nullGap = point.y === null && connectNulls === false;
  44221. var isPointInBreak = (!nullGap &&
  44222. (xAxis && xAxis.isInAnyBreak(point.x, true) ||
  44223. yAxis && yAxis.isInAnyBreak(point.y, true)));
  44224. // Set point.visible if in any break.
  44225. // If not in break, reset visible to original value.
  44226. point.visible = isPointInBreak ?
  44227. false :
  44228. point.options.visible !== false;
  44229. }
  44230. }
  44231. });
  44232. addEvent(Series, 'afterRender', function drawPointsWrapped() {
  44233. this.drawBreaks(this.xAxis, ['x']);
  44234. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  44235. });
  44236. /* eslint-enable no-invalid-this */
  44237. H.Series.prototype.drawBreaks = function (axis, keys) {
  44238. var series = this, points = series.points, breaks, threshold, eventName, y;
  44239. if (!axis) {
  44240. return; // #5950
  44241. }
  44242. keys.forEach(function (key) {
  44243. breaks = axis.breakArray || [];
  44244. threshold = axis.isXAxis ?
  44245. axis.min :
  44246. pick(series.options.threshold, axis.min);
  44247. points.forEach(function (point) {
  44248. y = pick(point['stack' + key.toUpperCase()], point[key]);
  44249. breaks.forEach(function (brk) {
  44250. eventName = false;
  44251. if ((threshold < brk.from &&
  44252. y > brk.to) ||
  44253. (threshold > brk.from &&
  44254. y < brk.from)) {
  44255. eventName = 'pointBreak';
  44256. }
  44257. else if ((threshold < brk.from &&
  44258. y > brk.from &&
  44259. y < brk.to) ||
  44260. (threshold > brk.from &&
  44261. y > brk.to &&
  44262. y < brk.from)) {
  44263. eventName = 'pointInBreak';
  44264. }
  44265. if (eventName) {
  44266. fireEvent(axis, eventName, { point: point, brk: brk });
  44267. }
  44268. });
  44269. });
  44270. });
  44271. };
  44272. /**
  44273. * Extend getGraphPath by identifying gaps in the data so that we can draw a gap
  44274. * in the line or area. This was moved from ordinal axis module to broken axis
  44275. * module as of #5045.
  44276. *
  44277. * @private
  44278. * @function Highcharts.Series#gappedPath
  44279. *
  44280. * @return {Highcharts.SVGPathArray}
  44281. * Gapped path
  44282. */
  44283. H.Series.prototype.gappedPath = function () {
  44284. var currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, gapSize = this.options.gapSize, points = this.points.slice(), i = points.length - 1, yAxis = this.yAxis, stack;
  44285. /**
  44286. * Defines when to display a gap in the graph, together with the
  44287. * [gapUnit](plotOptions.series.gapUnit) option.
  44288. *
  44289. * In case when `dataGrouping` is enabled, points can be grouped into a
  44290. * larger time span. This can make the grouped points to have a greater
  44291. * distance than the absolute value of `gapSize` property, which will result
  44292. * in disappearing graph completely. To prevent this situation the mentioned
  44293. * distance between grouped points is used instead of previously defined
  44294. * `gapSize`.
  44295. *
  44296. * In practice, this option is most often used to visualize gaps in
  44297. * time series. In a stock chart, intraday data is available for daytime
  44298. * hours, while gaps will appear in nights and weekends.
  44299. *
  44300. * @see [gapUnit](plotOptions.series.gapUnit)
  44301. * @see [xAxis.breaks](#xAxis.breaks)
  44302. *
  44303. * @sample {highstock} stock/plotoptions/series-gapsize/
  44304. * Setting the gap size to 2 introduces gaps for weekends in daily
  44305. * datasets.
  44306. *
  44307. * @type {number}
  44308. * @default 0
  44309. * @product highstock
  44310. * @requires modules/broken-axis
  44311. * @apioption plotOptions.series.gapSize
  44312. */
  44313. /**
  44314. * Together with [gapSize](plotOptions.series.gapSize), this option defines
  44315. * where to draw gaps in the graph.
  44316. *
  44317. * When the `gapUnit` is `relative` (default), a gap size of 5 means
  44318. * that if the distance between two points is greater than five times
  44319. * that of the two closest points, the graph will be broken.
  44320. *
  44321. * When the `gapUnit` is `value`, the gap is based on absolute axis values,
  44322. * which on a datetime axis is milliseconds. This also applies to the
  44323. * navigator series that inherits gap options from the base series.
  44324. *
  44325. * @see [gapSize](plotOptions.series.gapSize)
  44326. *
  44327. * @type {string}
  44328. * @default relative
  44329. * @since 5.0.13
  44330. * @product highstock
  44331. * @validvalue ["relative", "value"]
  44332. * @requires modules/broken-axis
  44333. * @apioption plotOptions.series.gapUnit
  44334. */
  44335. if (gapSize && i > 0) { // #5008
  44336. // Gap unit is relative
  44337. if (this.options.gapUnit !== 'value') {
  44338. gapSize *= this.basePointRange;
  44339. }
  44340. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  44341. if (groupingSize &&
  44342. groupingSize > gapSize &&
  44343. // Except when DG is forced (e.g. from other series)
  44344. // and has lower granularity than actual points (#11351)
  44345. groupingSize >= this.basePointRange) {
  44346. gapSize = groupingSize;
  44347. }
  44348. // extension for ordinal breaks
  44349. var current = void 0, next = void 0;
  44350. while (i--) {
  44351. // Reassign next if it is not visible
  44352. if (!(next && next.visible !== false)) {
  44353. next = points[i + 1];
  44354. }
  44355. current = points[i];
  44356. // Skip iteration if one of the points is not visible
  44357. if (next.visible === false || current.visible === false) {
  44358. continue;
  44359. }
  44360. if (next.x - current.x > gapSize) {
  44361. var xRange = (current.x + next.x) / 2;
  44362. points.splice(// insert after this one
  44363. i + 1, 0, {
  44364. isNull: true,
  44365. x: xRange
  44366. });
  44367. // For stacked chart generate empty stack items, #6546
  44368. if (this.options.stacking) {
  44369. stack = yAxis.stacks[this.stackKey][xRange] =
  44370. new H.StackItem(yAxis, yAxis.options
  44371. .stackLabels, false, xRange, this.stack);
  44372. stack.total = 0;
  44373. }
  44374. }
  44375. // Assign current to next for the upcoming iteration
  44376. next = current;
  44377. }
  44378. }
  44379. // Call base method
  44380. return this.getGraphPath(points);
  44381. };
  44382. });
  44383. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  44384. });
  44385. _registerModule(_modules, 'parts/DataGrouping.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  44386. /* *
  44387. *
  44388. * (c) 2010-2019 Torstein Honsi
  44389. *
  44390. * License: www.highcharts.com/license
  44391. *
  44392. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44393. *
  44394. * */
  44395. /**
  44396. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  44397. */
  44398. /**
  44399. * @interface Highcharts.DataGroupingInfoObject
  44400. */ /**
  44401. * @name Highcharts.DataGroupingInfoObject#length
  44402. * @type {number}
  44403. */ /**
  44404. * @name Highcharts.DataGroupingInfoObject#options
  44405. * @type {Highcharts.SeriesOptionsType|undefined}
  44406. */ /**
  44407. * @name Highcharts.DataGroupingInfoObject#start
  44408. * @type {number}
  44409. */
  44410. var arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, defined = U.defined, extend = U.extend, isNumber = U.isNumber, pick = U.pick;
  44411. var addEvent = H.addEvent, Axis = H.Axis, defaultPlotOptions = H.defaultPlotOptions, format = H.format, merge = H.merge, Point = H.Point, Series = H.Series, Tooltip = H.Tooltip;
  44412. /* ************************************************************************** *
  44413. * Start data grouping module *
  44414. * ************************************************************************** */
  44415. /* eslint-disable no-invalid-this, valid-jsdoc */
  44416. /**
  44417. * Define the available approximation types. The data grouping
  44418. * approximations takes an array or numbers as the first parameter. In case
  44419. * of ohlc, four arrays are sent in as four parameters. Each array consists
  44420. * only of numbers. In case null values belong to the group, the property
  44421. * .hasNulls will be set to true on the array.
  44422. *
  44423. * @product highstock
  44424. *
  44425. * @private
  44426. * @name Highcharts.approximations
  44427. * @type {Highcharts.Dictionary<Function>}
  44428. */
  44429. var approximations = H.approximations = {
  44430. sum: function (arr) {
  44431. var len = arr.length, ret;
  44432. // 1. it consists of nulls exclusive
  44433. if (!len && arr.hasNulls) {
  44434. ret = null;
  44435. // 2. it has a length and real values
  44436. }
  44437. else if (len) {
  44438. ret = 0;
  44439. while (len--) {
  44440. ret += arr[len];
  44441. }
  44442. }
  44443. // 3. it has zero length, so just return undefined
  44444. // => doNothing()
  44445. return ret;
  44446. },
  44447. average: function (arr) {
  44448. var len = arr.length, ret = approximations.sum(arr);
  44449. // If we have a number, return it divided by the length. If not,
  44450. // return null or undefined based on what the sum method finds.
  44451. if (isNumber(ret) && len) {
  44452. ret = correctFloat(ret / len);
  44453. }
  44454. return ret;
  44455. },
  44456. // The same as average, but for series with multiple values, like area
  44457. // ranges.
  44458. averages: function () {
  44459. var ret = [];
  44460. [].forEach.call(arguments, function (arr) {
  44461. ret.push(approximations.average(arr));
  44462. });
  44463. // Return undefined when first elem. is undefined and let
  44464. // sum method handle null (#7377)
  44465. return typeof ret[0] === 'undefined' ? void 0 : ret;
  44466. },
  44467. open: function (arr) {
  44468. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  44469. },
  44470. high: function (arr) {
  44471. return arr.length ?
  44472. arrayMax(arr) :
  44473. (arr.hasNulls ? null : void 0);
  44474. },
  44475. low: function (arr) {
  44476. return arr.length ?
  44477. arrayMin(arr) :
  44478. (arr.hasNulls ? null : void 0);
  44479. },
  44480. close: function (arr) {
  44481. return arr.length ?
  44482. arr[arr.length - 1] :
  44483. (arr.hasNulls ? null : void 0);
  44484. },
  44485. // ohlc and range are special cases where a multidimensional array is
  44486. // input and an array is output
  44487. ohlc: function (open, high, low, close) {
  44488. open = approximations.open(open);
  44489. high = approximations.high(high);
  44490. low = approximations.low(low);
  44491. close = approximations.close(close);
  44492. if (isNumber(open) ||
  44493. isNumber(high) ||
  44494. isNumber(low) ||
  44495. isNumber(close)) {
  44496. return [open, high, low, close];
  44497. }
  44498. // else, return is undefined
  44499. },
  44500. range: function (low, high) {
  44501. low = approximations.low(low);
  44502. high = approximations.high(high);
  44503. if (isNumber(low) || isNumber(high)) {
  44504. return [low, high];
  44505. }
  44506. if (low === null && high === null) {
  44507. return null;
  44508. }
  44509. // else, return is undefined
  44510. }
  44511. };
  44512. var groupData = function (xData, yData, groupPositions, approximation) {
  44513. var series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], groupedYData = [], groupMap = [], dataLength = xData.length, pointX, pointY, groupedY,
  44514. // when grouping the fake extended axis for panning,
  44515. // we don't need to consider y
  44516. handleYData = !!yData, values = [], approximationFn, pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']), pos = 0, start = 0, valuesLen, i, j;
  44517. /**
  44518. * @private
  44519. */
  44520. function getApproximation(approx) {
  44521. if (typeof approx === 'function') {
  44522. return approx;
  44523. }
  44524. if (approximations[approx]) {
  44525. return approximations[approx];
  44526. }
  44527. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  44528. 'average'];
  44529. }
  44530. approximationFn = getApproximation(approximation);
  44531. // Calculate values array size from pointArrayMap length
  44532. if (pointArrayMapLength) {
  44533. pointArrayMap.forEach(function () {
  44534. values.push([]);
  44535. });
  44536. }
  44537. else {
  44538. values.push([]);
  44539. }
  44540. valuesLen = pointArrayMapLength || 1;
  44541. // Start with the first point within the X axis range (#2696)
  44542. for (i = 0; i <= dataLength; i++) {
  44543. if (xData[i] >= groupPositions[0]) {
  44544. break;
  44545. }
  44546. }
  44547. for (i; i <= dataLength; i++) {
  44548. // when a new group is entered, summarize and initialize
  44549. // the previous group
  44550. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  44551. xData[i] >= groupPositions[pos + 1]) ||
  44552. i === dataLength) { // get the last group
  44553. // get group x and y
  44554. pointX = groupPositions[pos];
  44555. series.dataGroupInfo = {
  44556. start: series.cropStart + start,
  44557. length: values[0].length
  44558. };
  44559. groupedY = approximationFn.apply(series, values);
  44560. // By default, let options of the first grouped point be passed over
  44561. // to the grouped point. This allows preserving properties like
  44562. // `name` and `color` or custom properties. Implementers can
  44563. // override this from the approximation function, where they can
  44564. // write custom options to `this.dataGroupInfo.options`.
  44565. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  44566. // Convert numbers and arrays into objects
  44567. series.dataGroupInfo.options = merge(series.pointClass.prototype
  44568. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  44569. // Make sure the raw data (x, y, open, high etc) is not copied
  44570. // over and overwriting approximated data.
  44571. extendedPointArrayMap.forEach(function (key) {
  44572. delete series.dataGroupInfo.options[key];
  44573. });
  44574. }
  44575. // push the grouped data
  44576. if (typeof groupedY !== 'undefined') {
  44577. groupedXData.push(pointX);
  44578. groupedYData.push(groupedY);
  44579. groupMap.push(series.dataGroupInfo);
  44580. }
  44581. // reset the aggregate arrays
  44582. start = i;
  44583. for (j = 0; j < valuesLen; j++) {
  44584. values[j].length = 0; // faster than values[j] = []
  44585. values[j].hasNulls = false;
  44586. }
  44587. // Advance on the group positions
  44588. pos += 1;
  44589. // don't loop beyond the last group
  44590. if (i === dataLength) {
  44591. break;
  44592. }
  44593. }
  44594. // break out
  44595. if (i === dataLength) {
  44596. break;
  44597. }
  44598. // for each raw data point, push it to an array that contains all values
  44599. // for this specific group
  44600. if (pointArrayMap) {
  44601. var index = series.cropStart + i, point = (data && data[index]) ||
  44602. series.pointClass.prototype.applyOptions.apply({
  44603. series: series
  44604. }, [dataOptions[index]]), val;
  44605. for (j = 0; j < pointArrayMapLength; j++) {
  44606. val = point[pointArrayMap[j]];
  44607. if (isNumber(val)) {
  44608. values[j].push(val);
  44609. }
  44610. else if (val === null) {
  44611. values[j].hasNulls = true;
  44612. }
  44613. }
  44614. }
  44615. else {
  44616. pointY = handleYData ? yData[i] : null;
  44617. if (isNumber(pointY)) {
  44618. values[0].push(pointY);
  44619. }
  44620. else if (pointY === null) {
  44621. values[0].hasNulls = true;
  44622. }
  44623. }
  44624. }
  44625. return {
  44626. groupedXData: groupedXData,
  44627. groupedYData: groupedYData,
  44628. groupMap: groupMap
  44629. };
  44630. };
  44631. var dataGrouping = {
  44632. approximations: approximations,
  44633. groupData: groupData
  44634. };
  44635. // -----------------------------------------------------------------------------
  44636. // The following code applies to implementation of data grouping on a Series
  44637. var seriesProto = Series.prototype, baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  44638. /** @ignore */
  44639. commonOptions = {
  44640. // enabled: null, // (true for stock charts, false for basic),
  44641. // forced: undefined,
  44642. groupPixelWidth: 2,
  44643. // the first one is the point or start value, the second is the start
  44644. // value if we're dealing with range, the third one is the end value if
  44645. // dealing with a range
  44646. dateTimeLabelFormats: {
  44647. millisecond: [
  44648. '%A, %b %e, %H:%M:%S.%L',
  44649. '%A, %b %e, %H:%M:%S.%L',
  44650. '-%H:%M:%S.%L'
  44651. ],
  44652. second: [
  44653. '%A, %b %e, %H:%M:%S',
  44654. '%A, %b %e, %H:%M:%S',
  44655. '-%H:%M:%S'
  44656. ],
  44657. minute: [
  44658. '%A, %b %e, %H:%M',
  44659. '%A, %b %e, %H:%M',
  44660. '-%H:%M'
  44661. ],
  44662. hour: [
  44663. '%A, %b %e, %H:%M',
  44664. '%A, %b %e, %H:%M',
  44665. '-%H:%M'
  44666. ],
  44667. day: [
  44668. '%A, %b %e, %Y',
  44669. '%A, %b %e',
  44670. '-%A, %b %e, %Y'
  44671. ],
  44672. week: [
  44673. 'Week from %A, %b %e, %Y',
  44674. '%A, %b %e',
  44675. '-%A, %b %e, %Y'
  44676. ],
  44677. month: [
  44678. '%B %Y',
  44679. '%B',
  44680. '-%B %Y'
  44681. ],
  44682. year: [
  44683. '%Y',
  44684. '%Y',
  44685. '-%Y'
  44686. ]
  44687. }
  44688. // smoothed = false, // enable this for navigator series only
  44689. }, specificOptions = {
  44690. line: {},
  44691. spline: {},
  44692. area: {},
  44693. areaspline: {},
  44694. arearange: {},
  44695. column: {
  44696. groupPixelWidth: 10
  44697. },
  44698. columnrange: {
  44699. groupPixelWidth: 10
  44700. },
  44701. candlestick: {
  44702. groupPixelWidth: 10
  44703. },
  44704. ohlc: {
  44705. groupPixelWidth: 5
  44706. }
  44707. },
  44708. // units are defined in a separate array to allow complete overriding in
  44709. // case of a user option
  44710. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  44711. [
  44712. 'millisecond',
  44713. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  44714. ], [
  44715. 'second',
  44716. [1, 2, 5, 10, 15, 30]
  44717. ], [
  44718. 'minute',
  44719. [1, 2, 5, 10, 15, 30]
  44720. ], [
  44721. 'hour',
  44722. [1, 2, 3, 4, 6, 8, 12]
  44723. ], [
  44724. 'day',
  44725. [1]
  44726. ], [
  44727. 'week',
  44728. [1]
  44729. ], [
  44730. 'month',
  44731. [1, 3, 6]
  44732. ], [
  44733. 'year',
  44734. null
  44735. ]
  44736. ];
  44737. // Set default approximations to the prototypes if present. Properties are
  44738. // inherited down. Can be overridden for individual series types.
  44739. seriesProto.getDGApproximation = function () {
  44740. if (H.seriesTypes.arearange && this instanceof H.seriesTypes.arearange) {
  44741. return 'range';
  44742. }
  44743. if (H.seriesTypes.ohlc && this instanceof H.seriesTypes.ohlc) {
  44744. return 'ohlc';
  44745. }
  44746. if (H.seriesTypes.column && this instanceof H.seriesTypes.column) {
  44747. return 'sum';
  44748. }
  44749. return 'average';
  44750. };
  44751. /**
  44752. * Takes parallel arrays of x and y data and groups the data into intervals
  44753. * defined by groupPositions, a collection of starting x values for each group.
  44754. *
  44755. * @private
  44756. * @function Highcharts.Series#groupData
  44757. *
  44758. * @param {Array<number>} xData
  44759. *
  44760. * @param {Array<number>|Array<Array<number>>} yData
  44761. *
  44762. * @param {boolean} groupPositions
  44763. *
  44764. * @param {string|Function} approximation
  44765. *
  44766. * @return {void}
  44767. */
  44768. seriesProto.groupData = groupData;
  44769. // Extend the basic processData method, that crops the data to the current zoom
  44770. // range, with data grouping logic.
  44771. seriesProto.processData = function () {
  44772. var series = this, chart = series.chart, options = series.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  44773. pick(dataGroupingOptions.enabled, chart.options.isStock), visible = (series.visible || !chart.options.chart.ignoreHiddenSeries), hasGroupedData, skip, lastDataGrouping = this.currentDataGrouping, currentDataGrouping, croppedData, revertRequireSorting = false;
  44774. // Run base method
  44775. series.forceCrop = groupingEnabled; // #334
  44776. series.groupPixelWidth = null; // #2110
  44777. series.hasProcessed = true; // #2692
  44778. // Data needs to be sorted for dataGrouping
  44779. if (groupingEnabled && !series.requireSorting) {
  44780. series.requireSorting = revertRequireSorting = true;
  44781. }
  44782. // Skip if processData returns false or if grouping is disabled (in that
  44783. // order)
  44784. skip = (baseProcessData.apply(series, arguments) === false ||
  44785. !groupingEnabled);
  44786. // Revert original requireSorting value if changed
  44787. if (revertRequireSorting) {
  44788. series.requireSorting = false;
  44789. }
  44790. if (!skip) {
  44791. series.destroyGroupedData();
  44792. var i, processedXData = dataGroupingOptions.groupAll ?
  44793. series.xData :
  44794. series.processedXData, processedYData = dataGroupingOptions.groupAll ?
  44795. series.yData :
  44796. series.processedYData, plotSizeX = chart.plotSizeX, xAxis = series.xAxis, ordinal = xAxis.options.ordinal, groupPixelWidth = series.groupPixelWidth =
  44797. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  44798. // Execute grouping if the amount of points is greater than the limit
  44799. // defined in groupPixelWidth
  44800. if (groupPixelWidth) {
  44801. hasGroupedData = true;
  44802. // Force recreation of point instances in series.translate, #5699
  44803. series.isDirty = true;
  44804. series.points = null; // #6709
  44805. var extremes = xAxis.getExtremes(), xMin = extremes.min, xMax = extremes.max, groupIntervalFactor = (ordinal &&
  44806. xAxis.getGroupIntervalFactor(xMin, xMax, series)) || 1, interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  44807. groupIntervalFactor, groupPositions = xAxis.getTimeTicks(xAxis.normalizeTimeTickInterval(interval, dataGroupingOptions.units ||
  44808. defaultDataGroupingUnits),
  44809. // Processed data may extend beyond axis (#4907)
  44810. Math.min(xMin, processedXData[0]), Math.max(xMax, processedXData[processedXData.length - 1]), xAxis.options.startOfWeek, processedXData, series.closestPointRange), groupedData = seriesProto.groupData.apply(series, [
  44811. processedXData,
  44812. processedYData,
  44813. groupPositions,
  44814. dataGroupingOptions.approximation
  44815. ]), groupedXData = groupedData.groupedXData, groupedYData = groupedData.groupedYData, gapSize = 0;
  44816. // Prevent the smoothed data to spill out left and right, and make
  44817. // sure data is not shifted to the left
  44818. if (dataGroupingOptions.smoothed && groupedXData.length) {
  44819. i = groupedXData.length - 1;
  44820. groupedXData[i] = Math.min(groupedXData[i], xMax);
  44821. while (i-- && i > 0) {
  44822. groupedXData[i] += interval / 2;
  44823. }
  44824. groupedXData[0] = Math.max(groupedXData[0], xMin);
  44825. }
  44826. // Record what data grouping values were used
  44827. for (i = 1; i < groupPositions.length; i++) {
  44828. // The grouped gapSize needs to be the largest distance between
  44829. // the group to capture varying group sizes like months or DST
  44830. // crossing (#10000). Also check that the gap is not at the
  44831. // start of a segment.
  44832. if (!groupPositions.info.segmentStarts ||
  44833. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  44834. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  44835. }
  44836. }
  44837. currentDataGrouping = groupPositions.info;
  44838. currentDataGrouping.gapSize = gapSize;
  44839. series.closestPointRange = groupPositions.info.totalRange;
  44840. series.groupMap = groupedData.groupMap;
  44841. // Make sure the X axis extends to show the first group (#2533)
  44842. // But only for visible series (#5493, #6393)
  44843. if (defined(groupedXData[0]) &&
  44844. groupedXData[0] < xAxis.min &&
  44845. visible) {
  44846. if ((!defined(xAxis.options.min) &&
  44847. xAxis.min <= xAxis.dataMin) ||
  44848. xAxis.min === xAxis.dataMin) {
  44849. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  44850. }
  44851. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  44852. }
  44853. // We calculated all group positions but we should render
  44854. // only the ones within the visible range
  44855. if (dataGroupingOptions.groupAll) {
  44856. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  44857. );
  44858. groupedXData = croppedData.xData;
  44859. groupedYData = croppedData.yData;
  44860. }
  44861. // Set series props
  44862. series.processedXData = groupedXData;
  44863. series.processedYData = groupedYData;
  44864. }
  44865. else {
  44866. series.groupMap = null;
  44867. }
  44868. series.hasGroupedData = hasGroupedData;
  44869. series.currentDataGrouping = currentDataGrouping;
  44870. series.preventGraphAnimation =
  44871. (lastDataGrouping && lastDataGrouping.totalRange) !==
  44872. (currentDataGrouping && currentDataGrouping.totalRange);
  44873. }
  44874. };
  44875. // Destroy the grouped data points. #622, #740
  44876. seriesProto.destroyGroupedData = function () {
  44877. // Clear previous groups
  44878. if (this.groupedData) {
  44879. this.groupedData.forEach(function (point, i) {
  44880. if (point) {
  44881. this.groupedData[i] = point.destroy ?
  44882. point.destroy() : null;
  44883. }
  44884. }, this);
  44885. // Clears all:
  44886. // - `this.groupedData`
  44887. // - `this.points`
  44888. // - `preserve` object in series.update()
  44889. this.groupedData.length = 0;
  44890. }
  44891. };
  44892. // Override the generatePoints method by adding a reference to grouped data
  44893. seriesProto.generatePoints = function () {
  44894. baseGeneratePoints.apply(this);
  44895. // Record grouped data in order to let it be destroyed the next time
  44896. // processData runs
  44897. this.destroyGroupedData(); // #622
  44898. this.groupedData = this.hasGroupedData ? this.points : null;
  44899. };
  44900. // Override point prototype to throw a warning when trying to update grouped
  44901. // points.
  44902. addEvent(Point, 'update', function () {
  44903. if (this.dataGroup) {
  44904. H.error(24, false, this.series.chart);
  44905. return false;
  44906. }
  44907. });
  44908. // Extend the original method, make the tooltip's header reflect the grouped
  44909. // range.
  44910. addEvent(Tooltip, 'headerFormatter', function (e) {
  44911. 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'];
  44912. // apply only to grouped series
  44913. if (xAxis &&
  44914. xAxis.options.type === 'datetime' &&
  44915. dataGroupingOptions &&
  44916. isNumber(labelConfig.key)) {
  44917. // set variables
  44918. currentDataGrouping = series.currentDataGrouping;
  44919. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  44920. // Fallback to commonOptions (#9693)
  44921. commonOptions.dateTimeLabelFormats;
  44922. // if we have grouped data, use the grouping information to get the
  44923. // right format
  44924. if (currentDataGrouping) {
  44925. labelFormats =
  44926. dateTimeLabelFormats[currentDataGrouping.unitName];
  44927. if (currentDataGrouping.count === 1) {
  44928. xDateFormat = labelFormats[0];
  44929. }
  44930. else {
  44931. xDateFormat = labelFormats[1];
  44932. xDateFormatEnd = labelFormats[2];
  44933. }
  44934. // if not grouped, and we don't have set the xDateFormat option, get the
  44935. // best fit, so if the least distance between points is one minute, show
  44936. // it, but if the least distance is one day, skip hours and minutes etc.
  44937. }
  44938. else if (!xDateFormat && dateTimeLabelFormats) {
  44939. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  44940. }
  44941. // now format the key
  44942. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  44943. if (xDateFormatEnd) {
  44944. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  44945. }
  44946. // Replace default header style with class name
  44947. if (series.chart.styledMode) {
  44948. formatString = this.styledModeFormat(formatString);
  44949. }
  44950. // return the replaced format
  44951. e.text = format(formatString, {
  44952. point: extend(labelConfig.point, { key: formattedKey }),
  44953. series: series
  44954. }, chart);
  44955. e.preventDefault();
  44956. }
  44957. });
  44958. // Destroy grouped data on series destroy
  44959. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  44960. // Handle default options for data grouping. This must be set at runtime because
  44961. // some series types are defined after this.
  44962. addEvent(Series, 'afterSetOptions', function (e) {
  44963. var options = e.options, type = this.type, plotOptions = this.chart.options.plotOptions, defaultOptions = defaultPlotOptions[type].dataGrouping,
  44964. // External series, for example technical indicators should also
  44965. // inherit commonOptions which are not available outside this module
  44966. baseOptions = this.useCommonDataGrouping && commonOptions;
  44967. if (specificOptions[type] || baseOptions) { // #1284
  44968. if (!defaultOptions) {
  44969. defaultOptions = merge(commonOptions, specificOptions[type]);
  44970. }
  44971. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  44972. // Set by the StockChart constructor:
  44973. plotOptions[type].dataGrouping, this.userOptions.dataGrouping);
  44974. }
  44975. });
  44976. // When resetting the scale reset the hasProccessed flag to avoid taking
  44977. // previous data grouping of neighbour series into accound when determining
  44978. // group pixel width (#2692).
  44979. addEvent(Axis, 'afterSetScale', function () {
  44980. this.series.forEach(function (series) {
  44981. series.hasProcessed = false;
  44982. });
  44983. });
  44984. // Get the data grouping pixel width based on the greatest defined individual
  44985. // width of the axis' series, and if whether one of the axes need grouping.
  44986. Axis.prototype.getGroupPixelWidth = function () {
  44987. var series = this.series, len = series.length, i, groupPixelWidth = 0, doGrouping = false, dataLength, dgOptions;
  44988. // If multiple series are compared on the same x axis, give them the same
  44989. // group pixel width (#334)
  44990. i = len;
  44991. while (i--) {
  44992. dgOptions = series[i].options.dataGrouping;
  44993. if (dgOptions) {
  44994. groupPixelWidth = Math.max(groupPixelWidth,
  44995. // Fallback to commonOptions (#9693)
  44996. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  44997. }
  44998. }
  44999. // If one of the series needs grouping, apply it to all (#1634)
  45000. i = len;
  45001. while (i--) {
  45002. dgOptions = series[i].options.dataGrouping;
  45003. if (dgOptions && series[i].hasProcessed) { // #2692
  45004. dataLength = (series[i].processedXData || series[i].data).length;
  45005. // Execute grouping if the amount of points is greater than the
  45006. // limit defined in groupPixelWidth
  45007. if (series[i].groupPixelWidth ||
  45008. dataLength >
  45009. (this.chart.plotSizeX / groupPixelWidth) ||
  45010. (dataLength && dgOptions.forced)) {
  45011. doGrouping = true;
  45012. }
  45013. }
  45014. }
  45015. return doGrouping ? groupPixelWidth : 0;
  45016. };
  45017. /**
  45018. * Highstock only. Force data grouping on all the axis' series.
  45019. *
  45020. * @product highstock
  45021. *
  45022. * @function Highcharts.Axis#setDataGrouping
  45023. *
  45024. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  45025. * A `dataGrouping` configuration. Use `false` to disable data grouping
  45026. * dynamically.
  45027. *
  45028. * @param {boolean} [redraw=true]
  45029. * Whether to redraw the chart or wait for a later call to
  45030. * {@link Chart#redraw}.
  45031. *
  45032. * @return {void}
  45033. */
  45034. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  45035. var i;
  45036. redraw = pick(redraw, true);
  45037. if (!dataGrouping) {
  45038. dataGrouping = {
  45039. forced: false,
  45040. units: null
  45041. };
  45042. }
  45043. // Axis is instantiated, update all series
  45044. if (this instanceof Axis) {
  45045. i = this.series.length;
  45046. while (i--) {
  45047. this.series[i].update({
  45048. dataGrouping: dataGrouping
  45049. }, false);
  45050. }
  45051. // Axis not yet instanciated, alter series options
  45052. }
  45053. else {
  45054. this.chart.options.series.forEach(function (seriesOptions) {
  45055. seriesOptions.dataGrouping = dataGrouping;
  45056. }, false);
  45057. }
  45058. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  45059. this.ordinalSlope = null;
  45060. if (redraw) {
  45061. this.chart.redraw();
  45062. }
  45063. };
  45064. H.dataGrouping = dataGrouping;
  45065. /* eslint-enable no-invalid-this, valid-jsdoc */
  45066. /**
  45067. * Data grouping is the concept of sampling the data values into larger
  45068. * blocks in order to ease readability and increase performance of the
  45069. * JavaScript charts. Highstock by default applies data grouping when
  45070. * the points become closer than a certain pixel value, determined by
  45071. * the `groupPixelWidth` option.
  45072. *
  45073. * If data grouping is applied, the grouping information of grouped
  45074. * points can be read from the [Point.dataGroup](
  45075. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  45076. * the data itself are set, for example `name` or `color` or custom properties,
  45077. * the grouping logic doesn't know how to group it. In this case the options of
  45078. * the first point instance are copied over to the group point. This can be
  45079. * altered through a custom `approximation` callback function.
  45080. *
  45081. * @declare Highcharts.DataGroupingOptionsObject
  45082. * @product highstock
  45083. * @requires modules/datagrouping
  45084. * @apioption plotOptions.series.dataGrouping
  45085. */
  45086. /**
  45087. * The method of approximation inside a group. When for example 30 days
  45088. * are grouped into one month, this determines what value should represent
  45089. * the group. Possible values are "average", "averages", "open", "high",
  45090. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  45091. * is "ohlc" by default, which finds the open, high, low and close values
  45092. * within all the grouped data. For ranges, the approximation is "range",
  45093. * which finds the low and high values. For multi-dimensional data,
  45094. * like ranges and OHLC, "averages" will compute the average for each
  45095. * dimension.
  45096. *
  45097. * Custom aggregate methods can be added by assigning a callback function
  45098. * as the approximation. This function takes a numeric array as the
  45099. * argument and should return a single numeric value or `null`. Note
  45100. * that the numeric array will never contain null values, only true
  45101. * numbers. Instead, if null values are present in the raw data, the
  45102. * numeric array will have an `.hasNulls` property set to `true`. For
  45103. * single-value data sets the data is available in the first argument
  45104. * of the callback function. For OHLC data sets, all the open values
  45105. * are in the first argument, all high values in the second etc.
  45106. *
  45107. * Since v4.2.7, grouping meta data is available in the approximation
  45108. * callback from `this.dataGroupInfo`. It can be used to extract information
  45109. * from the raw data.
  45110. *
  45111. * Defaults to `average` for line-type series, `sum` for columns, `range`
  45112. * for range series and `ohlc` for OHLC and candlestick.
  45113. *
  45114. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  45115. * Approximation callback with custom data
  45116. *
  45117. * @type {Highcharts.DataGroupingApproximationValue|Function}
  45118. * @product highstock
  45119. * @apioption plotOptions.series.dataGrouping.approximation
  45120. */
  45121. /**
  45122. * Datetime formats for the header of the tooltip in a stock chart.
  45123. * The format can vary within a chart depending on the currently selected
  45124. * time range and the current data grouping.
  45125. *
  45126. * The default formats are:
  45127. * ```js
  45128. * {
  45129. * millisecond: [
  45130. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  45131. * ],
  45132. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  45133. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  45134. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  45135. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  45136. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  45137. * month: ['%B %Y', '%B', '-%B %Y'],
  45138. * year: ['%Y', '%Y', '-%Y']
  45139. * }
  45140. * ```
  45141. *
  45142. * For each of these array definitions, the first item is the format
  45143. * used when the active time span is one unit. For instance, if the
  45144. * current data applies to one week, the first item of the week array
  45145. * is used. The second and third items are used when the active time
  45146. * span is more than two units. For instance, if the current data applies
  45147. * to two weeks, the second and third item of the week array are used,
  45148. * and applied to the start and end date of the time span.
  45149. *
  45150. * @type {object}
  45151. * @product highstock
  45152. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  45153. */
  45154. /**
  45155. * Enable or disable data grouping.
  45156. *
  45157. * @type {boolean}
  45158. * @default true
  45159. * @product highstock
  45160. * @apioption plotOptions.series.dataGrouping.enabled
  45161. */
  45162. /**
  45163. * When data grouping is forced, it runs no matter how small the intervals
  45164. * are. This can be handy for example when the sum should be calculated
  45165. * for values appearing at random times within each hour.
  45166. *
  45167. * @type {boolean}
  45168. * @default false
  45169. * @product highstock
  45170. * @apioption plotOptions.series.dataGrouping.forced
  45171. */
  45172. /**
  45173. * The approximate pixel width of each group. If for example a series
  45174. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  45175. * is performed. If however the series contains so many points that
  45176. * the spacing is less than the groupPixelWidth, Highcharts will try
  45177. * to group it into appropriate groups so that each is more or less
  45178. * two pixels wide. If multiple series with different group pixel widths
  45179. * are drawn on the same x axis, all series will take the greatest width.
  45180. * For example, line series have 2px default group width, while column
  45181. * series have 10px. If combined, both the line and the column will
  45182. * have 10px by default.
  45183. *
  45184. * @type {number}
  45185. * @default 2
  45186. * @product highstock
  45187. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  45188. */
  45189. /**
  45190. * By default only points within the visible range are grouped. Enabling this
  45191. * option will force data grouping to calculate all grouped points for a given
  45192. * dataset. That option prevents for example a column series from calculating
  45193. * a grouped point partially. The effect is similar to
  45194. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  45195. * not affect yAxis extremes.
  45196. *
  45197. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  45198. * Two series with the same data but different groupAll setting
  45199. *
  45200. * @type {boolean}
  45201. * @default false
  45202. * @since 6.1.0
  45203. * @product highstock
  45204. * @apioption plotOptions.series.dataGrouping.groupAll
  45205. */
  45206. /**
  45207. * Normally, a group is indexed by the start of that group, so for example
  45208. * when 30 daily values are grouped into one month, that month's x value
  45209. * will be the 1st of the month. This apparently shifts the data to
  45210. * the left. When the smoothed option is true, this is compensated for.
  45211. * The data is shifted to the middle of the group, and min and max
  45212. * values are preserved. Internally, this is used in the Navigator series.
  45213. *
  45214. * @type {boolean}
  45215. * @default false
  45216. * @product highstock
  45217. * @apioption plotOptions.series.dataGrouping.smoothed
  45218. */
  45219. /**
  45220. * An array determining what time intervals the data is allowed to be
  45221. * grouped to. Each array item is an array where the first value is
  45222. * the time unit and the second value another array of allowed multiples.
  45223. *
  45224. * Defaults to:
  45225. * ```js
  45226. * units: [[
  45227. * 'millisecond', // unit name
  45228. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  45229. * ], [
  45230. * 'second',
  45231. * [1, 2, 5, 10, 15, 30]
  45232. * ], [
  45233. * 'minute',
  45234. * [1, 2, 5, 10, 15, 30]
  45235. * ], [
  45236. * 'hour',
  45237. * [1, 2, 3, 4, 6, 8, 12]
  45238. * ], [
  45239. * 'day',
  45240. * [1]
  45241. * ], [
  45242. * 'week',
  45243. * [1]
  45244. * ], [
  45245. * 'month',
  45246. * [1, 3, 6]
  45247. * ], [
  45248. * 'year',
  45249. * null
  45250. * ]]
  45251. * ```
  45252. *
  45253. * @type {Array<Array<string,(Array<number>|null)>>}
  45254. * @product highstock
  45255. * @apioption plotOptions.series.dataGrouping.units
  45256. */
  45257. /**
  45258. * The approximate pixel width of each group. If for example a series
  45259. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  45260. * is performed. If however the series contains so many points that
  45261. * the spacing is less than the groupPixelWidth, Highcharts will try
  45262. * to group it into appropriate groups so that each is more or less
  45263. * two pixels wide. Defaults to `10`.
  45264. *
  45265. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  45266. * Two series with the same data density but different groupPixelWidth
  45267. *
  45268. * @type {number}
  45269. * @default 10
  45270. * @product highstock
  45271. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  45272. */
  45273. ''; // required by JSDoc parsing
  45274. return dataGrouping;
  45275. });
  45276. _registerModule(_modules, 'parts/OHLCSeries.js', [_modules['parts/Globals.js']], function (H) {
  45277. /* *
  45278. *
  45279. * (c) 2010-2019 Torstein Honsi
  45280. *
  45281. * License: www.highcharts.com/license
  45282. *
  45283. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45284. *
  45285. * */
  45286. var Point = H.Point, seriesType = H.seriesType, seriesTypes = H.seriesTypes;
  45287. /**
  45288. * The ohlc series type.
  45289. *
  45290. * @private
  45291. * @class
  45292. * @name Highcharts.seriesTypes.ohlc
  45293. *
  45294. * @augments Highcharts.Series
  45295. */
  45296. seriesType('ohlc', 'column'
  45297. /**
  45298. * An OHLC chart is a style of financial chart used to describe price
  45299. * movements over time. It displays open, high, low and close values per
  45300. * data point.
  45301. *
  45302. * @sample stock/demo/ohlc/
  45303. * OHLC chart
  45304. *
  45305. * @extends plotOptions.column
  45306. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  45307. * stack
  45308. * @product highstock
  45309. * @optionparent plotOptions.ohlc
  45310. */
  45311. , {
  45312. /**
  45313. * The approximate pixel width of each group. If for example a series
  45314. * with 30 points is displayed over a 600 pixel wide plot area, no
  45315. * grouping is performed. If however the series contains so many points
  45316. * that the spacing is less than the groupPixelWidth, Highcharts will
  45317. * try to group it into appropriate groups so that each is more or less
  45318. * two pixels wide. Defaults to `5`.
  45319. *
  45320. * @type {number}
  45321. * @default 5
  45322. * @product highstock
  45323. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  45324. */
  45325. /**
  45326. * The pixel width of the line/border. Defaults to `1`.
  45327. *
  45328. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  45329. * A greater line width
  45330. *
  45331. * @type {number}
  45332. * @default 1
  45333. * @product highstock
  45334. *
  45335. * @private
  45336. */
  45337. lineWidth: 1,
  45338. tooltip: {
  45339. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  45340. '<b> {series.name}</b><br/>' +
  45341. 'Open: {point.open}<br/>' +
  45342. 'High: {point.high}<br/>' +
  45343. 'Low: {point.low}<br/>' +
  45344. 'Close: {point.close}<br/>'
  45345. },
  45346. threshold: null,
  45347. states: {
  45348. /**
  45349. * @extends plotOptions.column.states.hover
  45350. * @product highstock
  45351. */
  45352. hover: {
  45353. /**
  45354. * The pixel width of the line representing the OHLC point.
  45355. *
  45356. * @type {number}
  45357. * @default 3
  45358. * @product highstock
  45359. */
  45360. lineWidth: 3
  45361. }
  45362. },
  45363. /**
  45364. * Determines which one of `open`, `high`, `low`, `close` values should
  45365. * be represented as `point.y`, which is later used to set dataLabel
  45366. * position and [compare](#plotOptions.series.compare).
  45367. *
  45368. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  45369. * Possible values
  45370. *
  45371. * @type {string}
  45372. * @default close
  45373. * @validvalue ["open", "high", "low", "close"]
  45374. * @product highstock
  45375. * @apioption plotOptions.ohlc.pointValKey
  45376. */
  45377. /**
  45378. * @default close
  45379. * @apioption plotOptions.ohlc.colorKey
  45380. */
  45381. /**
  45382. * Line color for up points.
  45383. *
  45384. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  45385. * @product highstock
  45386. * @apioption plotOptions.ohlc.upColor
  45387. */
  45388. stickyTracking: true
  45389. },
  45390. /**
  45391. * @lends Highcharts.seriesTypes.ohlc
  45392. */
  45393. {
  45394. /* eslint-disable valid-jsdoc */
  45395. directTouch: false,
  45396. pointArrayMap: ['open', 'high', 'low', 'close'],
  45397. toYData: function (point) {
  45398. // return a plain array for speedy calculation
  45399. return [point.open, point.high, point.low, point.close];
  45400. },
  45401. pointValKey: 'close',
  45402. pointAttrToOptions: {
  45403. stroke: 'color',
  45404. 'stroke-width': 'lineWidth'
  45405. },
  45406. /**
  45407. * @private
  45408. * @function Highcarts.seriesTypes.ohlc#init
  45409. * @return {void}
  45410. */
  45411. init: function () {
  45412. seriesTypes.column.prototype.init.apply(this, arguments);
  45413. this.options.stacking = false; // #8817
  45414. },
  45415. /**
  45416. * Postprocess mapping between options and SVG attributes
  45417. *
  45418. * @private
  45419. * @function Highcharts.seriesTypes.ohlc#pointAttribs
  45420. * @param {Highcharts.OHLCPoint} point
  45421. * @param {string} state
  45422. * @return {Highcharts.SVGAttributes}
  45423. */
  45424. pointAttribs: function (point, state) {
  45425. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options;
  45426. delete attribs.fill;
  45427. if (!point.options.color &&
  45428. options.upColor &&
  45429. point.open < point.close) {
  45430. attribs.stroke = options.upColor;
  45431. }
  45432. return attribs;
  45433. },
  45434. /**
  45435. * Translate data points from raw values x and y to plotX and plotY
  45436. *
  45437. * @private
  45438. * @function Highcharts.seriesTypes.ohlc#translate
  45439. * @return {void}
  45440. */
  45441. translate: function () {
  45442. var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue, translated = [
  45443. 'plotOpen',
  45444. 'plotHigh',
  45445. 'plotLow',
  45446. 'plotClose',
  45447. 'yBottom'
  45448. ]; // translate OHLC for
  45449. seriesTypes.column.prototype.translate.apply(series);
  45450. // Do the translation
  45451. series.points.forEach(function (point) {
  45452. [point.open, point.high, point.low, point.close, point.low]
  45453. .forEach(function (value, i) {
  45454. if (value !== null) {
  45455. if (hasModifyValue) {
  45456. value = series.modifyValue(value);
  45457. }
  45458. point[translated[i]] =
  45459. yAxis.toPixels(value, true);
  45460. }
  45461. });
  45462. // Align the tooltip to the high value to avoid covering the
  45463. // point
  45464. point.tooltipPos[1] =
  45465. point.plotHigh + yAxis.pos - series.chart.plotTop;
  45466. });
  45467. },
  45468. /**
  45469. * Draw the data points
  45470. *
  45471. * @private
  45472. * @function Highcharts.seriesTypes.ohlc#drawPoints
  45473. * @return {void}
  45474. */
  45475. drawPoints: function () {
  45476. var series = this, points = series.points, chart = series.chart;
  45477. points.forEach(function (point) {
  45478. var plotOpen, plotClose, crispCorr, halfWidth, path, graphic = point.graphic, crispX, isNew = !graphic;
  45479. if (typeof point.plotY !== 'undefined') {
  45480. // Create and/or update the graphic
  45481. if (!graphic) {
  45482. point.graphic = graphic = chart.renderer.path()
  45483. .add(series.group);
  45484. }
  45485. if (!chart.styledMode) {
  45486. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  45487. }
  45488. // crisp vector coordinates
  45489. crispCorr = (graphic.strokeWidth() % 2) / 2;
  45490. // #2596:
  45491. crispX = Math.round(point.plotX) - crispCorr;
  45492. halfWidth = Math.round(point.shapeArgs.width / 2);
  45493. // the vertical stem
  45494. path = [
  45495. 'M',
  45496. crispX, Math.round(point.yBottom),
  45497. 'L',
  45498. crispX, Math.round(point.plotHigh)
  45499. ];
  45500. // open
  45501. if (point.open !== null) {
  45502. plotOpen = Math.round(point.plotOpen) + crispCorr;
  45503. path.push('M', crispX, plotOpen, 'L', crispX - halfWidth, plotOpen);
  45504. }
  45505. // close
  45506. if (point.close !== null) {
  45507. plotClose = Math.round(point.plotClose) + crispCorr;
  45508. path.push('M', crispX, plotClose, 'L', crispX + halfWidth, plotClose);
  45509. }
  45510. graphic[isNew ? 'attr' : 'animate']({ d: path })
  45511. .addClass(point.getClassName(), true);
  45512. }
  45513. });
  45514. },
  45515. animate: null // Disable animation
  45516. /* eslint-enable valid-jsdoc */
  45517. },
  45518. /**
  45519. * @lends Highcharts.seriesTypes.ohlc.prototype.pointClass.prototype
  45520. */
  45521. {
  45522. /* eslint-disable valid-jsdoc */
  45523. /**
  45524. * Extend the parent method by adding up or down to the class name.
  45525. * @private
  45526. * @function Highcharts.seriesTypes.ohlc#getClassName
  45527. * @return {string}
  45528. */
  45529. getClassName: function () {
  45530. return Point.prototype.getClassName.call(this) +
  45531. (this.open < this.close ?
  45532. ' highcharts-point-up' :
  45533. ' highcharts-point-down');
  45534. }
  45535. /* eslint-enable valid-jsdoc */
  45536. });
  45537. /**
  45538. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  45539. * specified, it is inherited from [chart.type](#chart.type).
  45540. *
  45541. * @extends series,plotOptions.ohlc
  45542. * @excluding dataParser, dataURL
  45543. * @product highstock
  45544. * @apioption series.ohlc
  45545. */
  45546. /**
  45547. * An array of data points for the series. For the `ohlc` series type,
  45548. * points can be given in the following ways:
  45549. *
  45550. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  45551. * to `x,open,high,low,close`. If the first value is a string, it is applied
  45552. * as the name of the point, and the `x` value is inferred. The `x` value can
  45553. * also be omitted, in which case the inner arrays should be of length 4\.
  45554. * Then the `x` value is automatically calculated, either starting at 0 and
  45555. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  45556. * series options.
  45557. * ```js
  45558. * data: [
  45559. * [0, 6, 5, 6, 7],
  45560. * [1, 9, 4, 8, 2],
  45561. * [2, 6, 3, 4, 10]
  45562. * ]
  45563. * ```
  45564. *
  45565. * 2. An array of objects with named values. The following snippet shows only a
  45566. * few settings, see the complete options set below. If the total number of
  45567. * data points exceeds the series'
  45568. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  45569. * available.
  45570. * ```js
  45571. * data: [{
  45572. * x: 1,
  45573. * open: 3,
  45574. * high: 4,
  45575. * low: 5,
  45576. * close: 2,
  45577. * name: "Point2",
  45578. * color: "#00FF00"
  45579. * }, {
  45580. * x: 1,
  45581. * open: 4,
  45582. * high: 3,
  45583. * low: 6,
  45584. * close: 7,
  45585. * name: "Point1",
  45586. * color: "#FF00FF"
  45587. * }]
  45588. * ```
  45589. *
  45590. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  45591. * @extends series.arearange.data
  45592. * @excluding y, marker
  45593. * @product highstock
  45594. * @apioption series.ohlc.data
  45595. */
  45596. /**
  45597. * The closing value of each data point.
  45598. *
  45599. * @type {number}
  45600. * @product highstock
  45601. * @apioption series.ohlc.data.close
  45602. */
  45603. /**
  45604. * The opening value of each data point.
  45605. *
  45606. * @type {number}
  45607. * @product highstock
  45608. * @apioption series.ohlc.data.open
  45609. */
  45610. ''; // adds doclets above to transpilat
  45611. });
  45612. _registerModule(_modules, 'parts/CandlestickSeries.js', [_modules['parts/Globals.js']], function (H) {
  45613. /* *
  45614. *
  45615. * (c) 2010-2019 Torstein Honsi
  45616. *
  45617. * License: www.highcharts.com/license
  45618. *
  45619. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45620. *
  45621. * */
  45622. var defaultPlotOptions = H.defaultPlotOptions, merge = H.merge, seriesType = H.seriesType, seriesTypes = H.seriesTypes;
  45623. /**
  45624. * A candlestick chart is a style of financial chart used to describe price
  45625. * movements over time.
  45626. *
  45627. * @sample stock/demo/candlestick/
  45628. * Candlestick chart
  45629. *
  45630. * @extends plotOptions.ohlc
  45631. * @excluding borderColor,borderRadius,borderWidth
  45632. * @product highstock
  45633. * @optionparent plotOptions.candlestick
  45634. */
  45635. var candlestickOptions = {
  45636. /**
  45637. * The specific line color for up candle sticks. The default is to inherit
  45638. * the general `lineColor` setting.
  45639. *
  45640. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  45641. * Candlestick line colors
  45642. *
  45643. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  45644. * @since 1.3.6
  45645. * @product highstock
  45646. * @apioption plotOptions.candlestick.upLineColor
  45647. */
  45648. /**
  45649. * @type {Highcharts.DataGroupingApproximationValue|Function}
  45650. * @default ohlc
  45651. * @product highstock
  45652. * @apioption plotOptions.candlestick.dataGrouping.approximation
  45653. */
  45654. states: {
  45655. /**
  45656. * @extends plotOptions.column.states.hover
  45657. * @product highstock
  45658. */
  45659. hover: {
  45660. /**
  45661. * The pixel width of the line/border around the candlestick.
  45662. *
  45663. * @product highstock
  45664. */
  45665. lineWidth: 2
  45666. }
  45667. },
  45668. /**
  45669. * @extends plotOptions.ohlc.tooltip
  45670. */
  45671. tooltip: defaultPlotOptions.ohlc.tooltip,
  45672. /**
  45673. * @type {number|null}
  45674. * @product highstock
  45675. */
  45676. threshold: null,
  45677. /**
  45678. * The color of the line/border of the candlestick.
  45679. *
  45680. * In styled mode, the line stroke can be set with the
  45681. * `.highcharts-candlestick-series .highcahrts-point` rule.
  45682. *
  45683. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  45684. *
  45685. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  45686. * Candlestick line colors
  45687. *
  45688. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  45689. * @default #000000
  45690. * @product highstock
  45691. */
  45692. lineColor: '#000000',
  45693. /**
  45694. * The pixel width of the candlestick line/border. Defaults to `1`.
  45695. *
  45696. *
  45697. * In styled mode, the line stroke width can be set with the
  45698. * `.highcharts-candlestick-series .highcahrts-point` rule.
  45699. *
  45700. * @product highstock
  45701. */
  45702. lineWidth: 1,
  45703. /**
  45704. * The fill color of the candlestick when values are rising.
  45705. *
  45706. * In styled mode, the up color can be set with the
  45707. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  45708. *
  45709. * @sample {highstock} stock/plotoptions/candlestick-color/
  45710. * Custom colors
  45711. * @sample {highstock} highcharts/css/candlestick/
  45712. * Colors in styled mode
  45713. *
  45714. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  45715. * @default #ffffff
  45716. * @product highstock
  45717. */
  45718. upColor: '#ffffff',
  45719. /**
  45720. * @product highstock
  45721. */
  45722. stickyTracking: true
  45723. };
  45724. /**
  45725. * The candlestick series type.
  45726. *
  45727. * @private
  45728. * @class
  45729. * @name Highcharts.seriesTypes.candlestick
  45730. *
  45731. * @augments Highcharts.seriesTypes.ohlc
  45732. */
  45733. seriesType('candlestick', 'ohlc', merge(defaultPlotOptions.column, candlestickOptions),
  45734. /**
  45735. * @lends seriesTypes.candlestick
  45736. */
  45737. {
  45738. /* eslint-disable valid-jsdoc */
  45739. /**
  45740. * Postprocess mapping between options and SVG attributes
  45741. *
  45742. * @private
  45743. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  45744. * @param {Highcharts.Point} point
  45745. * @param {string} [state]
  45746. * @return {Highcharts.SVGAttributes}
  45747. */
  45748. pointAttribs: function (point, state) {
  45749. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options, isUp = point.open < point.close, stroke = options.lineColor || this.color, stateOptions;
  45750. attribs['stroke-width'] = options.lineWidth;
  45751. attribs.fill = point.options.color ||
  45752. (isUp ? (options.upColor || this.color) : this.color);
  45753. attribs.stroke = point.options.lineColor ||
  45754. (isUp ? (options.upLineColor || stroke) : stroke);
  45755. // Select or hover states
  45756. if (state) {
  45757. stateOptions = options.states[state];
  45758. attribs.fill = stateOptions.color || attribs.fill;
  45759. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  45760. attribs['stroke-width'] =
  45761. stateOptions.lineWidth || attribs['stroke-width'];
  45762. }
  45763. return attribs;
  45764. },
  45765. /**
  45766. * Draw the data points.
  45767. *
  45768. * @private
  45769. * @function Highcharts.seriesTypes.candlestick#drawPoints
  45770. * @return {void}
  45771. */
  45772. drawPoints: function () {
  45773. var series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
  45774. points.forEach(function (point) {
  45775. var graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth, isNew = !graphic;
  45776. if (typeof point.plotY !== 'undefined') {
  45777. if (!graphic) {
  45778. point.graphic = graphic = chart.renderer.path()
  45779. .add(series.group);
  45780. }
  45781. if (!series.chart.styledMode) {
  45782. graphic
  45783. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  45784. .shadow(series.options.shadow);
  45785. }
  45786. // Crisp vector coordinates
  45787. crispCorr = (graphic.strokeWidth() % 2) / 2;
  45788. // #2596:
  45789. crispX = Math.round(point.plotX) - crispCorr;
  45790. plotOpen = point.plotOpen;
  45791. plotClose = point.plotClose;
  45792. topBox = Math.min(plotOpen, plotClose);
  45793. bottomBox = Math.max(plotOpen, plotClose);
  45794. halfWidth = Math.round(point.shapeArgs.width / 2);
  45795. hasTopWhisker = reversedYAxis ?
  45796. bottomBox !== point.yBottom :
  45797. Math.round(topBox) !==
  45798. Math.round(point.plotHigh);
  45799. hasBottomWhisker = reversedYAxis ?
  45800. Math.round(topBox) !==
  45801. Math.round(point.plotHigh) :
  45802. bottomBox !== point.yBottom;
  45803. topBox = Math.round(topBox) + crispCorr;
  45804. bottomBox = Math.round(bottomBox) + crispCorr;
  45805. // Create the path. Due to a bug in Chrome 49, the path is
  45806. // first instanciated with no values, then the values
  45807. // pushed. For unknown reasons, instanciating the path array
  45808. // with all the values would lead to a crash when updating
  45809. // frequently (#5193).
  45810. path = [];
  45811. path.push('M', crispX - halfWidth, bottomBox, 'L', crispX - halfWidth, topBox, 'L', crispX + halfWidth, topBox, 'L', crispX + halfWidth, bottomBox, 'Z', // Ensure a nice rectangle #2602
  45812. 'M', crispX, topBox, 'L',
  45813. // #460, #2094
  45814. crispX, hasTopWhisker ?
  45815. Math.round(reversedYAxis ?
  45816. point.yBottom :
  45817. point.plotHigh) :
  45818. topBox, 'M', crispX, bottomBox, 'L',
  45819. // #460, #2094
  45820. crispX, hasBottomWhisker ?
  45821. Math.round(reversedYAxis ?
  45822. point.plotHigh :
  45823. point.yBottom) :
  45824. bottomBox);
  45825. graphic[isNew ? 'attr' : 'animate']({ d: path })
  45826. .addClass(point.getClassName(), true);
  45827. }
  45828. });
  45829. /* eslint-enable valid-jsdoc */
  45830. }
  45831. });
  45832. /**
  45833. * A `candlestick` series. If the [type](#series.candlestick.type)
  45834. * option is not specified, it is inherited from [chart.type](
  45835. * #chart.type).
  45836. *
  45837. * @type {*}
  45838. * @extends series,plotOptions.candlestick
  45839. * @excluding dataParser, dataURL
  45840. * @product highstock
  45841. * @apioption series.candlestick
  45842. */
  45843. /**
  45844. * An array of data points for the series. For the `candlestick` series
  45845. * type, points can be given in the following ways:
  45846. *
  45847. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  45848. * to `x,open,high,low,close`. If the first value is a string, it is applied
  45849. * as the name of the point, and the `x` value is inferred. The `x` value can
  45850. * also be omitted, in which case the inner arrays should be of length 4.
  45851. * Then the `x` value is automatically calculated, either starting at 0 and
  45852. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  45853. * series options.
  45854. * ```js
  45855. * data: [
  45856. * [0, 7, 2, 0, 4],
  45857. * [1, 1, 4, 2, 8],
  45858. * [2, 3, 3, 9, 3]
  45859. * ]
  45860. * ```
  45861. *
  45862. * 2. An array of objects with named values. The following snippet shows only a
  45863. * few settings, see the complete options set below. If the total number of
  45864. * data points exceeds the series'
  45865. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  45866. * available.
  45867. * ```js
  45868. * data: [{
  45869. * x: 1,
  45870. * open: 9,
  45871. * high: 2,
  45872. * low: 4,
  45873. * close: 6,
  45874. * name: "Point2",
  45875. * color: "#00FF00"
  45876. * }, {
  45877. * x: 1,
  45878. * open: 1,
  45879. * high: 4,
  45880. * low: 7,
  45881. * close: 7,
  45882. * name: "Point1",
  45883. * color: "#FF00FF"
  45884. * }]
  45885. * ```
  45886. *
  45887. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  45888. * @extends series.ohlc.data
  45889. * @excluding y
  45890. * @product highstock
  45891. * @apioption series.candlestick.data
  45892. */
  45893. ''; // adds doclets above to transpilat
  45894. });
  45895. _registerModule(_modules, 'mixins/on-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  45896. /* *
  45897. *
  45898. * (c) 2010-2019 Torstein Honsi
  45899. *
  45900. * License: www.highcharts.com/license
  45901. *
  45902. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45903. *
  45904. * */
  45905. var defined = U.defined;
  45906. var seriesTypes = H.seriesTypes, stableSort = H.stableSort;
  45907. /**
  45908. * @private
  45909. * @mixin onSeriesMixin
  45910. */
  45911. var onSeriesMixin = {
  45912. /* eslint-disable valid-jsdoc */
  45913. /**
  45914. * Override getPlotBox. If the onSeries option is valid, return the plot box
  45915. * of the onSeries, otherwise proceed as usual.
  45916. *
  45917. * @private
  45918. * @function onSeriesMixin.getPlotBox
  45919. * @return {Highcharts.SeriesPlotBoxObject}
  45920. */
  45921. getPlotBox: function () {
  45922. return H.Series.prototype.getPlotBox.call((this.options.onSeries &&
  45923. this.chart.get(this.options.onSeries)) || this);
  45924. },
  45925. /**
  45926. * Extend the translate method by placing the point on the related series
  45927. *
  45928. * @private
  45929. * @function onSeriesMixin.translate
  45930. * @return {void}
  45931. */
  45932. translate: function () {
  45933. seriesTypes.column.prototype.translate.apply(this);
  45934. var series = this, options = series.options, chart = series.chart, points = series.points, cursor = points.length - 1, point, lastPoint, optionsOnSeries = options.onSeries, onSeries = (optionsOnSeries &&
  45935. 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;
  45936. // relate to a master series
  45937. if (onSeries && onSeries.visible && i) {
  45938. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  45939. currentDataGrouping = onSeries.currentDataGrouping;
  45940. lastX = (onData[i - 1].x +
  45941. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  45942. // sort the data points
  45943. stableSort(points, function (a, b) {
  45944. return (a.x - b.x);
  45945. });
  45946. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  45947. while (i-- && points[cursor]) {
  45948. leftPoint = onData[i];
  45949. point = points[cursor];
  45950. point.y = leftPoint.y;
  45951. if (leftPoint.x <= point.x &&
  45952. typeof leftPoint[onKey] !== 'undefined') {
  45953. if (point.x <= lastX) { // #803
  45954. point.plotY = leftPoint[onKey];
  45955. // interpolate between points, #666
  45956. if (leftPoint.x < point.x &&
  45957. !step) {
  45958. rightPoint = onData[i + 1];
  45959. if (rightPoint &&
  45960. typeof rightPoint[onKey] !== 'undefined') {
  45961. // the distance ratio, between 0 and 1
  45962. distanceRatio =
  45963. (point.x - leftPoint.x) /
  45964. (rightPoint.x - leftPoint.x);
  45965. point.plotY +=
  45966. distanceRatio *
  45967. // the plotY distance
  45968. (rightPoint[onKey] - leftPoint[onKey]);
  45969. point.y +=
  45970. distanceRatio *
  45971. (rightPoint.y - leftPoint.y);
  45972. }
  45973. }
  45974. }
  45975. cursor--;
  45976. i++; // check again for points in the same x position
  45977. if (cursor < 0) {
  45978. break;
  45979. }
  45980. }
  45981. }
  45982. }
  45983. // Add plotY position and handle stacking
  45984. points.forEach(function (point, i) {
  45985. var stackIndex;
  45986. point.plotX += xOffset; // #2049
  45987. // Undefined plotY means the point is either on axis, outside series
  45988. // range or hidden series. If the series is outside the range of the
  45989. // x axis it should fall through with an undefined plotY, but then
  45990. // we must remove the shapeArgs (#847). For inverted charts, we need
  45991. // to calculate position anyway, because series.invertGroups is not
  45992. // defined
  45993. if (typeof point.plotY === 'undefined' || inverted) {
  45994. if (point.plotX >= 0 &&
  45995. point.plotX <= xAxis.len) {
  45996. // We're inside xAxis range
  45997. if (inverted) {
  45998. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  45999. point.plotX = defined(point.y) ?
  46000. yAxis.translate(point.y, 0, 0, 0, 1) :
  46001. 0;
  46002. }
  46003. else {
  46004. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  46005. xAxis.offset; // For the windbarb demo
  46006. }
  46007. }
  46008. else {
  46009. point.shapeArgs = {}; // 847
  46010. }
  46011. }
  46012. // if multiple flags appear at the same x, order them into a stack
  46013. lastPoint = points[i - 1];
  46014. if (lastPoint && lastPoint.plotX === point.plotX) {
  46015. if (typeof lastPoint.stackIndex === 'undefined') {
  46016. lastPoint.stackIndex = 0;
  46017. }
  46018. stackIndex = lastPoint.stackIndex + 1;
  46019. }
  46020. point.stackIndex = stackIndex; // #3639
  46021. });
  46022. this.onSeries = onSeries;
  46023. }
  46024. /* eslint-enable valid-jsdoc */
  46025. };
  46026. return onSeriesMixin;
  46027. });
  46028. _registerModule(_modules, 'parts/FlagsSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['mixins/on-series.js']], function (H, U, onSeriesMixin) {
  46029. /* *
  46030. *
  46031. * (c) 2010-2019 Torstein Honsi
  46032. *
  46033. * License: www.highcharts.com/license
  46034. *
  46035. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46036. *
  46037. * */
  46038. /**
  46039. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  46040. */
  46041. var defined = U.defined, isNumber = U.isNumber, objectEach = U.objectEach, wrap = U.wrap;
  46042. var addEvent = H.addEvent, merge = H.merge, noop = H.noop, Renderer = H.Renderer, Series = H.Series, seriesType = H.seriesType, SVGRenderer = H.SVGRenderer, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
  46043. /**
  46044. * The Flags series.
  46045. *
  46046. * @private
  46047. * @class
  46048. * @name Highcharts.seriesTypes.flags
  46049. *
  46050. * @augments Highcharts.Series
  46051. */
  46052. seriesType('flags', 'column'
  46053. /**
  46054. * Flags are used to mark events in stock charts. They can be added on the
  46055. * timeline, or attached to a specific series.
  46056. *
  46057. * @sample stock/demo/flags-general/
  46058. * Flags on a line series
  46059. *
  46060. * @extends plotOptions.column
  46061. * @excluding animation, borderColor, borderRadius, borderWidth,
  46062. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  46063. * turboThreshold
  46064. * @product highstock
  46065. * @optionparent plotOptions.flags
  46066. */
  46067. , {
  46068. /**
  46069. * In case the flag is placed on a series, on what point key to place
  46070. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  46071. * however, the flag can optionally be placed on the `open`, `high`,
  46072. * `low` or `close` key.
  46073. *
  46074. * @sample {highstock} stock/plotoptions/flags-onkey/
  46075. * Range series, flag on high
  46076. *
  46077. * @type {string}
  46078. * @default y
  46079. * @since 4.2.2
  46080. * @product highstock
  46081. * @validvalue ["y", "open", "high", "low", "close"]
  46082. * @apioption plotOptions.flags.onKey
  46083. */
  46084. /**
  46085. * The id of the series that the flags should be drawn on. If no id
  46086. * is given, the flags are drawn on the x axis.
  46087. *
  46088. * @sample {highstock} stock/plotoptions/flags/
  46089. * Flags on series and on x axis
  46090. *
  46091. * @type {string}
  46092. * @product highstock
  46093. * @apioption plotOptions.flags.onSeries
  46094. */
  46095. pointRange: 0,
  46096. /**
  46097. * Whether the flags are allowed to overlap sideways. If `false`, the
  46098. * flags are moved sideways using an algorithm that seeks to place every
  46099. * flag as close as possible to its original position.
  46100. *
  46101. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  46102. * Allow sideways overlap
  46103. *
  46104. * @since 6.0.4
  46105. */
  46106. allowOverlapX: false,
  46107. /**
  46108. * The shape of the marker. Can be one of "flag", "circlepin",
  46109. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  46110. * Individual shapes can also be set for each point.
  46111. *
  46112. * @sample {highstock} stock/plotoptions/flags/
  46113. * Different shapes
  46114. *
  46115. * @type {Highcharts.FlagsShapeValue}
  46116. * @product highstock
  46117. */
  46118. shape: 'flag',
  46119. /**
  46120. * When multiple flags in the same series fall on the same value, this
  46121. * number determines the vertical offset between them.
  46122. *
  46123. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  46124. * A greater stack distance
  46125. *
  46126. * @product highstock
  46127. */
  46128. stackDistance: 12,
  46129. /**
  46130. * Text alignment for the text inside the flag.
  46131. *
  46132. * @since 5.0.0
  46133. * @product highstock
  46134. * @validvalue ["left", "center", "right"]
  46135. */
  46136. textAlign: 'center',
  46137. /**
  46138. * Specific tooltip options for flag series. Flag series tooltips are
  46139. * different from most other types in that a flag doesn't have a data
  46140. * value, so the tooltip rather displays the `text` option for each
  46141. * point.
  46142. *
  46143. * @extends plotOptions.series.tooltip
  46144. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  46145. * @product highstock
  46146. */
  46147. tooltip: {
  46148. pointFormat: '{point.text}<br/>'
  46149. },
  46150. threshold: null,
  46151. /**
  46152. * The text to display on each flag. This can be defined on series
  46153. * level, or individually for each point. Defaults to `"A"`.
  46154. *
  46155. * @type {string}
  46156. * @default A
  46157. * @product highstock
  46158. * @apioption plotOptions.flags.title
  46159. */
  46160. /**
  46161. * The y position of the top left corner of the flag relative to either
  46162. * the series (if onSeries is defined), or the x axis. Defaults to
  46163. * `-30`.
  46164. *
  46165. * @product highstock
  46166. */
  46167. y: -30,
  46168. /**
  46169. * Whether to use HTML to render the flag texts. Using HTML allows for
  46170. * advanced formatting, images and reliable bi-directional text
  46171. * rendering. Note that exported images won't respect the HTML, and that
  46172. * HTML won't respect Z-index settings.
  46173. *
  46174. * @type {boolean}
  46175. * @default false
  46176. * @since 1.3
  46177. * @product highstock
  46178. * @apioption plotOptions.flags.useHTML
  46179. */
  46180. /**
  46181. * Fixed width of the flag's shape. By default, width is autocalculated
  46182. * according to the flag's title.
  46183. *
  46184. * @sample {highstock} stock/demo/flags-shapes/
  46185. * Flags with fixed width
  46186. *
  46187. * @type {number}
  46188. * @product highstock
  46189. * @apioption plotOptions.flags.width
  46190. */
  46191. /**
  46192. * Fixed height of the flag's shape. By default, height is
  46193. * autocalculated according to the flag's title.
  46194. *
  46195. * @type {number}
  46196. * @product highstock
  46197. * @apioption plotOptions.flags.height
  46198. */
  46199. /**
  46200. * The fill color for the flags.
  46201. *
  46202. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  46203. * @product highstock
  46204. */
  46205. fillColor: '#ffffff',
  46206. /**
  46207. * The color of the line/border of the flag.
  46208. *
  46209. * In styled mode, the stroke is set in the
  46210. * `.highcharts-flag-series.highcharts-point` rule.
  46211. *
  46212. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  46213. * @default #000000
  46214. * @product highstock
  46215. * @apioption plotOptions.flags.lineColor
  46216. */
  46217. /**
  46218. * The pixel width of the flag's line/border.
  46219. *
  46220. * @product highstock
  46221. */
  46222. lineWidth: 1,
  46223. states: {
  46224. /**
  46225. * @extends plotOptions.column.states.hover
  46226. * @product highstock
  46227. */
  46228. hover: {
  46229. /**
  46230. * The color of the line/border of the flag.
  46231. *
  46232. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  46233. * @product highstock
  46234. */
  46235. lineColor: '#000000',
  46236. /**
  46237. * The fill or background color of the flag.
  46238. *
  46239. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  46240. * @product highstock
  46241. */
  46242. fillColor: '#ccd6eb'
  46243. }
  46244. },
  46245. /**
  46246. * The text styles of the flag.
  46247. *
  46248. * In styled mode, the styles are set in the
  46249. * `.highcharts-flag-series .highcharts-point` rule.
  46250. *
  46251. * @type {Highcharts.CSSObject}
  46252. * @default {"fontSize": "11px", "fontWeight": "bold"}
  46253. * @product highstock
  46254. */
  46255. style: {
  46256. /** @ignore-option */
  46257. fontSize: '11px',
  46258. /** @ignore-option */
  46259. fontWeight: 'bold'
  46260. }
  46261. },
  46262. /**
  46263. * @lends seriesTypes.flags.prototype
  46264. */
  46265. {
  46266. sorted: false,
  46267. noSharedTooltip: true,
  46268. allowDG: false,
  46269. takeOrdinalPosition: false,
  46270. trackerGroups: ['markerGroup'],
  46271. forceCrop: true,
  46272. /* eslint-disable no-invalid-this, valid-jsdoc */
  46273. /**
  46274. * Inherit the initialization from base Series.
  46275. *
  46276. * @private
  46277. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  46278. */
  46279. init: Series.prototype.init,
  46280. /**
  46281. * Get presentational attributes
  46282. *
  46283. * @private
  46284. * @function Highcharts.seriesTypes.flags#pointAttribs
  46285. *
  46286. * @param {Highcharts.Point} point
  46287. *
  46288. * @param {string} [state]
  46289. *
  46290. * @return {Highcharts.SVGAttributes}
  46291. */
  46292. pointAttribs: function (point, state) {
  46293. var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  46294. if (state) {
  46295. fill = options.states[state].fillColor;
  46296. lineColor = options.states[state].lineColor;
  46297. lineWidth = options.states[state].lineWidth;
  46298. }
  46299. return {
  46300. fill: fill || color,
  46301. stroke: lineColor || color,
  46302. 'stroke-width': lineWidth || options.lineWidth || 0
  46303. };
  46304. },
  46305. translate: onSeriesMixin.translate,
  46306. getPlotBox: onSeriesMixin.getPlotBox,
  46307. /**
  46308. * Draw the markers.
  46309. *
  46310. * @private
  46311. * @function Highcharts.seriesTypes.flags#drawPoints
  46312. * @return {void}
  46313. */
  46314. drawPoints: function () {
  46315. 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;
  46316. i = points.length;
  46317. while (i--) {
  46318. point = points[i];
  46319. outsideRight =
  46320. (inverted ? point.plotY : point.plotX) >
  46321. series.xAxis.len;
  46322. plotX = point.plotX;
  46323. stackIndex = point.stackIndex;
  46324. shape = point.options.shape || options.shape;
  46325. plotY = point.plotY;
  46326. if (typeof plotY !== 'undefined') {
  46327. plotY = point.plotY + optionsY -
  46328. (typeof stackIndex !== 'undefined' &&
  46329. (stackIndex * options.stackDistance));
  46330. }
  46331. // skip connectors for higher level stacked points
  46332. point.anchorX = stackIndex ? void 0 : point.plotX;
  46333. anchorY = stackIndex ? void 0 : point.plotY;
  46334. centered = shape !== 'flag';
  46335. graphic = point.graphic;
  46336. // Only draw the point if y is defined and the flag is within
  46337. // the visible area
  46338. if (typeof plotY !== 'undefined' &&
  46339. plotX >= 0 &&
  46340. !outsideRight) {
  46341. // Create the flag
  46342. if (!graphic) {
  46343. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
  46344. if (!chart.styledMode) {
  46345. graphic
  46346. .attr(series.pointAttribs(point))
  46347. .css(merge(options.style, point.style));
  46348. }
  46349. graphic.attr({
  46350. align: centered ? 'center' : 'left',
  46351. width: options.width,
  46352. height: options.height,
  46353. 'text-align': options.textAlign
  46354. })
  46355. .addClass('highcharts-point')
  46356. .add(series.markerGroup);
  46357. // Add reference to the point for tracker (#6303)
  46358. if (point.graphic.div) {
  46359. point.graphic.div.point = point;
  46360. }
  46361. if (!chart.styledMode) {
  46362. graphic.shadow(options.shadow);
  46363. }
  46364. graphic.isNew = true;
  46365. }
  46366. if (plotX > 0) { // #3119
  46367. plotX -= graphic.strokeWidth() % 2; // #4285
  46368. }
  46369. // Plant the flag
  46370. attribs = {
  46371. y: plotY,
  46372. anchorY: anchorY
  46373. };
  46374. if (options.allowOverlapX) {
  46375. attribs.x = plotX;
  46376. attribs.anchorX = point.anchorX;
  46377. }
  46378. graphic.attr({
  46379. text: point.options.title || options.title || 'A'
  46380. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  46381. // Rig for the distribute function
  46382. if (!options.allowOverlapX) {
  46383. if (!boxesMap[point.plotX]) {
  46384. boxesMap[point.plotX] = {
  46385. align: centered ? 0.5 : 0,
  46386. size: graphic.width,
  46387. target: plotX,
  46388. anchorX: plotX
  46389. };
  46390. }
  46391. else {
  46392. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  46393. }
  46394. }
  46395. // Set the tooltip anchor position
  46396. point.tooltipPos = [
  46397. plotX,
  46398. plotY + yAxis.pos - chart.plotTop
  46399. ]; // #6327
  46400. }
  46401. else if (graphic) {
  46402. point.graphic = graphic.destroy();
  46403. }
  46404. }
  46405. // Handle X-dimension overlapping
  46406. if (!options.allowOverlapX) {
  46407. objectEach(boxesMap, function (box) {
  46408. box.plotX = box.anchorX;
  46409. boxes.push(box);
  46410. });
  46411. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  46412. points.forEach(function (point) {
  46413. var box = point.graphic && boxesMap[point.plotX];
  46414. if (box) {
  46415. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  46416. x: box.pos + box.align * box.size,
  46417. anchorX: point.anchorX
  46418. });
  46419. // Hide flag when its box position is not specified
  46420. // (#8573, #9299)
  46421. if (!defined(box.pos)) {
  46422. point.graphic.attr({
  46423. x: -9999,
  46424. anchorX: -9999
  46425. });
  46426. point.graphic.isNew = true;
  46427. }
  46428. else {
  46429. point.graphic.isNew = false;
  46430. }
  46431. }
  46432. });
  46433. }
  46434. // Can be a mix of SVG and HTML and we need events for both (#6303)
  46435. if (options.useHTML) {
  46436. wrap(series.markerGroup, 'on', function (proceed) {
  46437. return H.SVGElement.prototype.on.apply(
  46438. // for HTML
  46439. proceed.apply(this, [].slice.call(arguments, 1)),
  46440. // and for SVG
  46441. [].slice.call(arguments, 1));
  46442. });
  46443. }
  46444. },
  46445. /**
  46446. * Extend the column trackers with listeners to expand and contract
  46447. * stacks.
  46448. *
  46449. * @private
  46450. * @function Highcharts.seriesTypes.flags#drawTracker
  46451. * @return {void}
  46452. */
  46453. drawTracker: function () {
  46454. var series = this, points = series.points;
  46455. TrackerMixin.drawTrackerPoint.apply(this);
  46456. /* *
  46457. * Bring each stacked flag up on mouse over, this allows readability
  46458. * of vertically stacked elements as well as tight points on the x
  46459. * axis. #1924.
  46460. */
  46461. points.forEach(function (point) {
  46462. var graphic = point.graphic;
  46463. if (graphic) {
  46464. addEvent(graphic.element, 'mouseover', function () {
  46465. // Raise this point
  46466. if (point.stackIndex > 0 &&
  46467. !point.raised) {
  46468. point._y = graphic.y;
  46469. graphic.attr({
  46470. y: point._y - 8
  46471. });
  46472. point.raised = true;
  46473. }
  46474. // Revert other raised points
  46475. points.forEach(function (otherPoint) {
  46476. if (otherPoint !== point &&
  46477. otherPoint.raised &&
  46478. otherPoint.graphic) {
  46479. otherPoint.graphic.attr({
  46480. y: otherPoint._y
  46481. });
  46482. otherPoint.raised = false;
  46483. }
  46484. });
  46485. });
  46486. }
  46487. });
  46488. },
  46489. /**
  46490. * Disable animation, but keep clipping (#8546).
  46491. *
  46492. * @private
  46493. * @function Highcharts.seriesTypes.flags#animate
  46494. * @param {boolean} [init]
  46495. * @return {void}
  46496. */
  46497. animate: function (init) {
  46498. if (init) {
  46499. this.setClip();
  46500. }
  46501. else {
  46502. this.animate = null;
  46503. }
  46504. },
  46505. /**
  46506. * @private
  46507. * @function Highcharts.seriesTypes.flags#setClip
  46508. * @return {void}
  46509. */
  46510. setClip: function () {
  46511. Series.prototype.setClip.apply(this, arguments);
  46512. if (this.options.clip !== false && this.sharedClipKey) {
  46513. this.markerGroup
  46514. .clip(this.chart[this.sharedClipKey]);
  46515. }
  46516. },
  46517. /**
  46518. * @private
  46519. * @function Highcharts.seriesTypes.flags#buildKDTree
  46520. */
  46521. buildKDTree: noop,
  46522. /**
  46523. * Don't invert the flag marker group (#4960).
  46524. *
  46525. * @private
  46526. * @function Highcharts.seriesTypes.flags#invertGroups
  46527. */
  46528. invertGroups: noop
  46529. /* eslint-enable no-invalid-this, valid-jsdoc */
  46530. },
  46531. /**
  46532. * @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
  46533. */
  46534. {
  46535. isValid: function () {
  46536. // #9233 - Prevent from treating flags as null points (even if
  46537. // they have no y values defined).
  46538. return isNumber(this.y) || typeof this.y === 'undefined';
  46539. }
  46540. });
  46541. // create the flag icon with anchor
  46542. symbols.flag = function (x, y, w, h, options) {
  46543. var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  46544. return symbols.circle(anchorX - 1, anchorY - 1, 2, 2).concat([
  46545. 'M', anchorX, anchorY,
  46546. 'L', x, y + h,
  46547. x, y,
  46548. x + w, y,
  46549. x + w, y + h,
  46550. x, y + h,
  46551. 'Z'
  46552. ]);
  46553. };
  46554. /**
  46555. * Create the circlepin and squarepin icons with anchor.
  46556. * @private
  46557. * @param {string} shape - circle or square
  46558. * @return {void}
  46559. */
  46560. function createPinSymbol(shape) {
  46561. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  46562. var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path, labelTopOrBottomY;
  46563. // For single-letter flags, make sure circular flags are not taller
  46564. // than their width
  46565. if (shape === 'circle' && h > w) {
  46566. x -= Math.round((h - w) / 2);
  46567. w = h;
  46568. }
  46569. path = symbols[shape](x, y, w, h);
  46570. if (anchorX && anchorY) {
  46571. /**
  46572. * If the label is below the anchor, draw the connecting line
  46573. * from the top edge of the label
  46574. * otherwise start drawing from the bottom edge
  46575. */
  46576. labelTopOrBottomY = (y > anchorY) ? y : y + h;
  46577. path.push('M', shape === 'circle' ?
  46578. x + w / 2 :
  46579. path[1] + path[4] / 2, labelTopOrBottomY, 'L', anchorX, anchorY);
  46580. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  46581. }
  46582. return path;
  46583. };
  46584. }
  46585. createPinSymbol('circle');
  46586. createPinSymbol('square');
  46587. /**
  46588. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  46589. * Even VML browsers need this in order to generate shapes in export. Now share
  46590. * them with the VMLRenderer.
  46591. */
  46592. if (Renderer === VMLRenderer) {
  46593. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  46594. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  46595. });
  46596. }
  46597. /**
  46598. * A `flags` series. If the [type](#series.flags.type) option is not
  46599. * specified, it is inherited from [chart.type](#chart.type).
  46600. *
  46601. * @extends series,plotOptions.flags
  46602. * @excluding dataParser, dataURL
  46603. * @product highstock
  46604. * @apioption series.flags
  46605. */
  46606. /**
  46607. * An array of data points for the series. For the `flags` series type,
  46608. * points can be given in the following ways:
  46609. *
  46610. * 1. An array of objects with named values. The following snippet shows only a
  46611. * few settings, see the complete options set below. If the total number of
  46612. * data points exceeds the series'
  46613. * [turboThreshold](#series.flags.turboThreshold), this option is not
  46614. * available.
  46615. * ```js
  46616. * data: [{
  46617. * x: 1,
  46618. * title: "A",
  46619. * text: "First event"
  46620. * }, {
  46621. * x: 1,
  46622. * title: "B",
  46623. * text: "Second event"
  46624. * }]
  46625. * ```
  46626. *
  46627. * @type {Array<*>}
  46628. * @extends series.line.data
  46629. * @excluding dataLabels, marker, name, y
  46630. * @product highstock
  46631. * @apioption series.flags.data
  46632. */
  46633. /**
  46634. * The fill color of an individual flag. By default it inherits from
  46635. * the series color.
  46636. *
  46637. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  46638. * @product highstock
  46639. * @apioption series.flags.data.fillColor
  46640. */
  46641. /**
  46642. * The longer text to be shown in the flag's tooltip.
  46643. *
  46644. * @type {string}
  46645. * @product highstock
  46646. * @apioption series.flags.data.text
  46647. */
  46648. /**
  46649. * The short text to be shown on the flag.
  46650. *
  46651. * @type {string}
  46652. * @product highstock
  46653. * @apioption series.flags.data.title
  46654. */
  46655. ''; // adds doclets above to transpiled file
  46656. });
  46657. _registerModule(_modules, 'parts/RangeSelector.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  46658. /* *
  46659. *
  46660. * (c) 2010-2019 Torstein Honsi
  46661. *
  46662. * License: www.highcharts.com/license
  46663. *
  46664. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46665. *
  46666. * */
  46667. /**
  46668. * Define the time span for the button
  46669. *
  46670. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  46671. */
  46672. /**
  46673. * Callback function to react on button clicks.
  46674. *
  46675. * @callback Highcharts.RangeSelectorClickCallbackFunction
  46676. *
  46677. * @param {global.Event} e
  46678. * Event arguments.
  46679. *
  46680. * @param {boolean|undefined}
  46681. * Return false to cancel the default button event.
  46682. */
  46683. /**
  46684. * Callback function to parse values entered in the input boxes and return a
  46685. * valid JavaScript time as milliseconds since 1970.
  46686. *
  46687. * @callback Highcharts.RangeSelectorParseCallbackFunction
  46688. *
  46689. * @param {string} value
  46690. * Input value to parse.
  46691. *
  46692. * @return {number}
  46693. * Parsed JavaScript time value.
  46694. */
  46695. var defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, discardElement = U.discardElement, extend = U.extend, isNumber = U.isNumber, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, splat = U.splat;
  46696. var addEvent = H.addEvent, Axis = H.Axis, Chart = H.Chart, css = H.css, createElement = H.createElement, defaultOptions = H.defaultOptions, fireEvent = H.fireEvent, merge = H.merge;
  46697. /* ************************************************************************** *
  46698. * Start Range Selector code *
  46699. * ************************************************************************** */
  46700. extend(defaultOptions, {
  46701. /**
  46702. * The range selector is a tool for selecting ranges to display within
  46703. * the chart. It provides buttons to select preconfigured ranges in
  46704. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  46705. * boxes where min and max dates can be manually input.
  46706. *
  46707. * @product highstock gantt
  46708. * @optionparent rangeSelector
  46709. */
  46710. rangeSelector: {
  46711. /**
  46712. * Whether to enable all buttons from the start. By default buttons are
  46713. * only enabled if the corresponding time range exists on the X axis,
  46714. * but enabling all buttons allows for dynamically loading different
  46715. * time ranges.
  46716. *
  46717. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  46718. * All buttons enabled
  46719. *
  46720. * @type {boolean}
  46721. * @default false
  46722. * @since 2.0.3
  46723. * @apioption rangeSelector.allButtonsEnabled
  46724. */
  46725. /**
  46726. * An array of configuration objects for the buttons.
  46727. *
  46728. * Defaults to:
  46729. * ```js
  46730. * buttons: [{
  46731. * type: 'month',
  46732. * count: 1,
  46733. * text: '1m'
  46734. * }, {
  46735. * type: 'month',
  46736. * count: 3,
  46737. * text: '3m'
  46738. * }, {
  46739. * type: 'month',
  46740. * count: 6,
  46741. * text: '6m'
  46742. * }, {
  46743. * type: 'ytd',
  46744. * text: 'YTD'
  46745. * }, {
  46746. * type: 'year',
  46747. * count: 1,
  46748. * text: '1y'
  46749. * }, {
  46750. * type: 'all',
  46751. * text: 'All'
  46752. * }]
  46753. * ```
  46754. *
  46755. * @sample {highstock} stock/rangeselector/datagrouping/
  46756. * Data grouping by buttons
  46757. *
  46758. * @type {Array<*>}
  46759. * @apioption rangeSelector.buttons
  46760. */
  46761. /**
  46762. * How many units of the defined type the button should span. If `type`
  46763. * is "month" and `count` is 3, the button spans three months.
  46764. *
  46765. * @type {number}
  46766. * @default 1
  46767. * @apioption rangeSelector.buttons.count
  46768. */
  46769. /**
  46770. * Fires when clicking on the rangeSelector button. One parameter,
  46771. * event, is passed to the function, containing common event
  46772. * information.
  46773. *
  46774. * ```js
  46775. * click: function(e) {
  46776. * console.log(this);
  46777. * }
  46778. * ```
  46779. *
  46780. * Return false to stop default button's click action.
  46781. *
  46782. * @sample {highstock} stock/rangeselector/button-click/
  46783. * Click event on the button
  46784. *
  46785. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  46786. * @apioption rangeSelector.buttons.events.click
  46787. */
  46788. /**
  46789. * Additional range (in milliseconds) added to the end of the calculated
  46790. * time span.
  46791. *
  46792. * @sample {highstock} stock/rangeselector/min-max-offsets/
  46793. * Button offsets
  46794. *
  46795. * @type {number}
  46796. * @default 0
  46797. * @since 6.0.0
  46798. * @apioption rangeSelector.buttons.offsetMax
  46799. */
  46800. /**
  46801. * Additional range (in milliseconds) added to the start of the
  46802. * calculated time span.
  46803. *
  46804. * @sample {highstock} stock/rangeselector/min-max-offsets/
  46805. * Button offsets
  46806. *
  46807. * @type {number}
  46808. * @default 0
  46809. * @since 6.0.0
  46810. * @apioption rangeSelector.buttons.offsetMin
  46811. */
  46812. /**
  46813. * When buttons apply dataGrouping on a series, by default zooming
  46814. * in/out will deselect buttons and unset dataGrouping. Enable this
  46815. * option to keep buttons selected when extremes change.
  46816. *
  46817. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  46818. * Different preserveDataGrouping settings
  46819. *
  46820. * @type {boolean}
  46821. * @default false
  46822. * @since 6.1.2
  46823. * @apioption rangeSelector.buttons.preserveDataGrouping
  46824. */
  46825. /**
  46826. * A custom data grouping object for each button.
  46827. *
  46828. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  46829. *
  46830. * @sample {highstock} stock/rangeselector/datagrouping/
  46831. * Data grouping by range selector buttons
  46832. *
  46833. * @type {*}
  46834. * @extends plotOptions.series.dataGrouping
  46835. * @apioption rangeSelector.buttons.dataGrouping
  46836. */
  46837. /**
  46838. * The text for the button itself.
  46839. *
  46840. * @type {string}
  46841. * @apioption rangeSelector.buttons.text
  46842. */
  46843. /**
  46844. * Defined the time span for the button. Can be one of `millisecond`,
  46845. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  46846. * and `all`.
  46847. *
  46848. * @type {Highcharts.RangeSelectorButtonTypeValue}
  46849. * @apioption rangeSelector.buttons.type
  46850. */
  46851. /**
  46852. * The space in pixels between the buttons in the range selector.
  46853. *
  46854. * @type {number}
  46855. * @default 0
  46856. * @apioption rangeSelector.buttonSpacing
  46857. */
  46858. /**
  46859. * Enable or disable the range selector.
  46860. *
  46861. * @sample {highstock} stock/rangeselector/enabled/
  46862. * Disable the range selector
  46863. *
  46864. * @type {boolean}
  46865. * @default true
  46866. * @apioption rangeSelector.enabled
  46867. */
  46868. /**
  46869. * The vertical alignment of the rangeselector box. Allowed properties
  46870. * are `top`, `middle`, `bottom`.
  46871. *
  46872. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  46873. * Middle
  46874. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  46875. * Bottom
  46876. *
  46877. * @type {Highcharts.VerticalAlignValue}
  46878. * @since 6.0.0
  46879. */
  46880. verticalAlign: 'top',
  46881. /**
  46882. * A collection of attributes for the buttons. The object takes SVG
  46883. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  46884. * a collection of CSS properties for the text.
  46885. *
  46886. * The object can also be extended with states, so you can set
  46887. * presentational options for `hover`, `select` or `disabled` button
  46888. * states.
  46889. *
  46890. * CSS styles for the text label.
  46891. *
  46892. * In styled mode, the buttons are styled by the
  46893. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  46894. * different states.
  46895. *
  46896. * @sample {highstock} stock/rangeselector/styling/
  46897. * Styling the buttons and inputs
  46898. *
  46899. * @type {Highcharts.SVGAttributes}
  46900. */
  46901. buttonTheme: {
  46902. /** @ignore */
  46903. width: 28,
  46904. /** @ignore */
  46905. height: 18,
  46906. /** @ignore */
  46907. padding: 2,
  46908. /** @ignore */
  46909. zIndex: 7 // #484, #852
  46910. },
  46911. /**
  46912. * When the rangeselector is floating, the plot area does not reserve
  46913. * space for it. This opens for positioning anywhere on the chart.
  46914. *
  46915. * @sample {highstock} stock/rangeselector/floating/
  46916. * Placing the range selector between the plot area and the
  46917. * navigator
  46918. *
  46919. * @since 6.0.0
  46920. */
  46921. floating: false,
  46922. /**
  46923. * The x offset of the range selector relative to its horizontal
  46924. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  46925. *
  46926. * @since 6.0.0
  46927. */
  46928. x: 0,
  46929. /**
  46930. * The y offset of the range selector relative to its horizontal
  46931. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  46932. *
  46933. * @since 6.0.0
  46934. */
  46935. y: 0,
  46936. /**
  46937. * Deprecated. The height of the range selector. Currently it is
  46938. * calculated dynamically.
  46939. *
  46940. * @deprecated
  46941. * @type {number|undefined}
  46942. * @since 2.1.9
  46943. */
  46944. height: void 0,
  46945. /**
  46946. * The border color of the date input boxes.
  46947. *
  46948. * @sample {highstock} stock/rangeselector/styling/
  46949. * Styling the buttons and inputs
  46950. *
  46951. * @type {Highcharts.ColorString}
  46952. * @default #cccccc
  46953. * @since 1.3.7
  46954. * @apioption rangeSelector.inputBoxBorderColor
  46955. */
  46956. /**
  46957. * The pixel height of the date input boxes.
  46958. *
  46959. * @sample {highstock} stock/rangeselector/styling/
  46960. * Styling the buttons and inputs
  46961. *
  46962. * @type {number}
  46963. * @default 17
  46964. * @since 1.3.7
  46965. * @apioption rangeSelector.inputBoxHeight
  46966. */
  46967. /**
  46968. * CSS for the container DIV holding the input boxes. Deprecated as
  46969. * of 1.2.5\. Use [inputPosition](#rangeSelector.inputPosition) instead.
  46970. *
  46971. * @sample {highstock} stock/rangeselector/styling/
  46972. * Styling the buttons and inputs
  46973. *
  46974. * @deprecated
  46975. * @type {Highcharts.CSSObject}
  46976. * @apioption rangeSelector.inputBoxStyle
  46977. */
  46978. /**
  46979. * The pixel width of the date input boxes.
  46980. *
  46981. * @sample {highstock} stock/rangeselector/styling/
  46982. * Styling the buttons and inputs
  46983. *
  46984. * @type {number}
  46985. * @default 90
  46986. * @since 1.3.7
  46987. * @apioption rangeSelector.inputBoxWidth
  46988. */
  46989. /**
  46990. * The date format in the input boxes when not selected for editing.
  46991. * Defaults to `%b %e, %Y`.
  46992. *
  46993. * @sample {highstock} stock/rangeselector/input-format/
  46994. * Milliseconds in the range selector
  46995. *
  46996. * @type {string}
  46997. * @default %b %e, %Y
  46998. * @apioption rangeSelector.inputDateFormat
  46999. */
  47000. /**
  47001. * A custom callback function to parse values entered in the input boxes
  47002. * and return a valid JavaScript time as milliseconds since 1970.
  47003. *
  47004. * @sample {highstock} stock/rangeselector/input-format/
  47005. * Milliseconds in the range selector
  47006. *
  47007. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  47008. * @since 1.3.3
  47009. * @apioption rangeSelector.inputDateParser
  47010. */
  47011. /**
  47012. * The date format in the input boxes when they are selected for
  47013. * editing. This must be a format that is recognized by JavaScript
  47014. * Date.parse.
  47015. *
  47016. * @sample {highstock} stock/rangeselector/input-format/
  47017. * Milliseconds in the range selector
  47018. *
  47019. * @type {string}
  47020. * @default %Y-%m-%d
  47021. * @apioption rangeSelector.inputEditDateFormat
  47022. */
  47023. /**
  47024. * Enable or disable the date input boxes. Defaults to enabled when
  47025. * there is enough space, disabled if not (typically mobile).
  47026. *
  47027. * @sample {highstock} stock/rangeselector/input-datepicker/
  47028. * Extending the input with a jQuery UI datepicker
  47029. *
  47030. * @type {boolean}
  47031. * @default true
  47032. * @apioption rangeSelector.inputEnabled
  47033. */
  47034. /**
  47035. * Positioning for the input boxes. Allowed properties are `align`,
  47036. * `x` and `y`.
  47037. *
  47038. * @since 1.2.4
  47039. */
  47040. inputPosition: {
  47041. /**
  47042. * The alignment of the input box. Allowed properties are `left`,
  47043. * `center`, `right`.
  47044. *
  47045. * @sample {highstock} stock/rangeselector/input-button-position/
  47046. * Alignment
  47047. *
  47048. * @type {Highcharts.AlignValue}
  47049. * @since 6.0.0
  47050. */
  47051. align: 'right',
  47052. /**
  47053. * X offset of the input row.
  47054. */
  47055. x: 0,
  47056. /**
  47057. * Y offset of the input row.
  47058. */
  47059. y: 0
  47060. },
  47061. /**
  47062. * The index of the button to appear pre-selected.
  47063. *
  47064. * @type {number}
  47065. * @apioption rangeSelector.selected
  47066. */
  47067. /**
  47068. * Positioning for the button row.
  47069. *
  47070. * @since 1.2.4
  47071. */
  47072. buttonPosition: {
  47073. /**
  47074. * The alignment of the input box. Allowed properties are `left`,
  47075. * `center`, `right`.
  47076. *
  47077. * @sample {highstock} stock/rangeselector/input-button-position/
  47078. * Alignment
  47079. *
  47080. * @type {Highcharts.AlignValue}
  47081. * @since 6.0.0
  47082. */
  47083. align: 'left',
  47084. /**
  47085. * X offset of the button row.
  47086. */
  47087. x: 0,
  47088. /**
  47089. * Y offset of the button row.
  47090. */
  47091. y: 0
  47092. },
  47093. /**
  47094. * CSS for the HTML inputs in the range selector.
  47095. *
  47096. * In styled mode, the inputs are styled by the
  47097. * `.highcharts-range-input text` rule in SVG mode, and
  47098. * `input.highcharts-range-selector` when active.
  47099. *
  47100. * @sample {highstock} stock/rangeselector/styling/
  47101. * Styling the buttons and inputs
  47102. *
  47103. * @type {Highcharts.CSSObject}
  47104. * @apioption rangeSelector.inputStyle
  47105. */
  47106. /**
  47107. * CSS styles for the labels - the Zoom, From and To texts.
  47108. *
  47109. * In styled mode, the labels are styled by the
  47110. * `.highcharts-range-label` class.
  47111. *
  47112. * @sample {highstock} stock/rangeselector/styling/
  47113. * Styling the buttons and inputs
  47114. *
  47115. * @type {Highcharts.CSSObject}
  47116. */
  47117. labelStyle: {
  47118. /** @ignore */
  47119. color: '#666666'
  47120. }
  47121. }
  47122. });
  47123. defaultOptions.lang = merge(defaultOptions.lang,
  47124. /**
  47125. * Language object. The language object is global and it can't be set
  47126. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  47127. * set it before any chart is initialized.
  47128. *
  47129. * ```js
  47130. * Highcharts.setOptions({
  47131. * lang: {
  47132. * months: [
  47133. * 'Janvier', 'Février', 'Mars', 'Avril',
  47134. * 'Mai', 'Juin', 'Juillet', 'Août',
  47135. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  47136. * ],
  47137. * weekdays: [
  47138. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  47139. * 'Jeudi', 'Vendredi', 'Samedi'
  47140. * ]
  47141. * }
  47142. * });
  47143. * ```
  47144. *
  47145. * @optionparent lang
  47146. */
  47147. {
  47148. /**
  47149. * The text for the label for the range selector buttons.
  47150. *
  47151. * @product highstock gantt
  47152. */
  47153. rangeSelectorZoom: 'Zoom',
  47154. /**
  47155. * The text for the label for the "from" input box in the range
  47156. * selector.
  47157. *
  47158. * @product highstock gantt
  47159. */
  47160. rangeSelectorFrom: 'From',
  47161. /**
  47162. * The text for the label for the "to" input box in the range selector.
  47163. *
  47164. * @product highstock gantt
  47165. */
  47166. rangeSelectorTo: 'To'
  47167. });
  47168. /* eslint-disable no-invalid-this, valid-jsdoc */
  47169. /**
  47170. * The range selector.
  47171. *
  47172. * @private
  47173. * @class
  47174. * @name Highcharts.RangeSelector
  47175. * @param {Highcharts.Chart} chart
  47176. */
  47177. function RangeSelector(chart) {
  47178. // Run RangeSelector
  47179. this.init(chart);
  47180. }
  47181. RangeSelector.prototype = {
  47182. /**
  47183. * The method to run when one of the buttons in the range selectors is
  47184. * clicked
  47185. *
  47186. * @private
  47187. * @function Highcharts.RangeSelector#clickButton
  47188. * @param {number} i
  47189. * The index of the button
  47190. * @param {boolean} [redraw]
  47191. * @return {void}
  47192. */
  47193. clickButton: function (i, redraw) {
  47194. 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
  47195. type = rangeOptions.type, baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, dataGrouping = rangeOptions.dataGrouping;
  47196. // chart has no data, base series is removed
  47197. if (dataMin === null || dataMax === null) {
  47198. return;
  47199. }
  47200. // Set the fixed range before range is altered
  47201. chart.fixedRange = range;
  47202. // Apply dataGrouping associated to button
  47203. if (dataGrouping) {
  47204. this.forcedDataGrouping = true;
  47205. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  47206. this.frozenStates = rangeOptions.preserveDataGrouping;
  47207. }
  47208. // Apply range
  47209. if (type === 'month' || type === 'year') {
  47210. if (!baseAxis) {
  47211. // This is set to the user options and picked up later when the
  47212. // axis is instantiated so that we know the min and max.
  47213. range = rangeOptions;
  47214. }
  47215. else {
  47216. ctx = {
  47217. range: rangeOptions,
  47218. max: newMax,
  47219. chart: chart,
  47220. dataMin: dataMin,
  47221. dataMax: dataMax
  47222. };
  47223. newMin = baseAxis.minFromRange.call(ctx);
  47224. if (isNumber(ctx.newMax)) {
  47225. newMax = ctx.newMax;
  47226. }
  47227. }
  47228. // Fixed times like minutes, hours, days
  47229. }
  47230. else if (range) {
  47231. newMin = Math.max(newMax - range, dataMin);
  47232. newMax = Math.min(newMin + range, dataMax);
  47233. }
  47234. else if (type === 'ytd') {
  47235. // On user clicks on the buttons, or a delayed action running from
  47236. // the beforeRender event (below), the baseAxis is defined.
  47237. if (baseAxis) {
  47238. // When "ytd" is the pre-selected button for the initial view,
  47239. // its calculation is delayed and rerun in the beforeRender
  47240. // event (below). When the series are initialized, but before
  47241. // the chart is rendered, we have access to the xData array
  47242. // (#942).
  47243. if (typeof dataMax === 'undefined') {
  47244. dataMin = Number.MAX_VALUE;
  47245. dataMax = Number.MIN_VALUE;
  47246. chart.series.forEach(function (series) {
  47247. // reassign it to the last item
  47248. var xData = series.xData;
  47249. dataMin = Math.min(xData[0], dataMin);
  47250. dataMax = Math.max(xData[xData.length - 1], dataMax);
  47251. });
  47252. redraw = false;
  47253. }
  47254. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  47255. newMin = rangeMin = ytdExtremes.min;
  47256. newMax = ytdExtremes.max;
  47257. // "ytd" is pre-selected. We don't yet have access to processed
  47258. // point and extremes data (things like pointStart and pointInterval
  47259. // are missing), so we delay the process (#942)
  47260. }
  47261. else {
  47262. rangeSelector.deferredYTDClick = i;
  47263. return;
  47264. }
  47265. }
  47266. else if (type === 'all' && baseAxis) {
  47267. newMin = dataMin;
  47268. newMax = dataMax;
  47269. }
  47270. newMin += rangeOptions._offsetMin;
  47271. newMax += rangeOptions._offsetMax;
  47272. rangeSelector.setSelected(i);
  47273. // Update the chart
  47274. if (!baseAxis) {
  47275. // Axis not yet instanciated. Temporarily set min and range
  47276. // options and remove them on chart load (#4317).
  47277. baseXAxisOptions = splat(chart.options.xAxis)[0];
  47278. rangeSetting = baseXAxisOptions.range;
  47279. baseXAxisOptions.range = range;
  47280. minSetting = baseXAxisOptions.min;
  47281. baseXAxisOptions.min = rangeMin;
  47282. addEvent(chart, 'load', function resetMinAndRange() {
  47283. baseXAxisOptions.range = rangeSetting;
  47284. baseXAxisOptions.min = minSetting;
  47285. });
  47286. }
  47287. else {
  47288. // Existing axis object. Set extremes after render time.
  47289. baseAxis.setExtremes(newMin, newMax, pick(redraw, 1), null, // auto animation
  47290. {
  47291. trigger: 'rangeSelectorButton',
  47292. rangeSelectorButton: rangeOptions
  47293. });
  47294. }
  47295. },
  47296. /**
  47297. * Set the selected option. This method only sets the internal flag, it
  47298. * doesn't update the buttons or the actual zoomed range.
  47299. *
  47300. * @private
  47301. * @function Highcharts.RangeSelector#setSelected
  47302. * @param {number} [selected]
  47303. * @return {void}
  47304. */
  47305. setSelected: function (selected) {
  47306. this.selected = this.options.selected = selected;
  47307. },
  47308. /**
  47309. * The default buttons for pre-selecting time frames
  47310. */
  47311. defaultButtons: [{
  47312. type: 'month',
  47313. count: 1,
  47314. text: '1m'
  47315. }, {
  47316. type: 'month',
  47317. count: 3,
  47318. text: '3m'
  47319. }, {
  47320. type: 'month',
  47321. count: 6,
  47322. text: '6m'
  47323. }, {
  47324. type: 'ytd',
  47325. text: 'YTD'
  47326. }, {
  47327. type: 'year',
  47328. count: 1,
  47329. text: '1y'
  47330. }, {
  47331. type: 'all',
  47332. text: 'All'
  47333. }],
  47334. /**
  47335. * Initialize the range selector
  47336. *
  47337. * @private
  47338. * @function Highcharts.RangeSelector#init
  47339. * @param {Highcharts.Chart} chart
  47340. * @return {void}
  47341. */
  47342. init: function (chart) {
  47343. var rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = options.buttons ||
  47344. [].concat(rangeSelector.defaultButtons), selectedOption = options.selected, blurInputs = function () {
  47345. var minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;
  47346. // #3274 in some case blur is not defined
  47347. if (minInput && minInput.blur) {
  47348. fireEvent(minInput, 'blur');
  47349. }
  47350. if (maxInput && maxInput.blur) {
  47351. fireEvent(maxInput, 'blur');
  47352. }
  47353. };
  47354. rangeSelector.chart = chart;
  47355. rangeSelector.options = options;
  47356. rangeSelector.buttons = [];
  47357. rangeSelector.buttonOptions = buttonOptions;
  47358. this.unMouseDown = addEvent(chart.container, 'mousedown', blurInputs);
  47359. this.unResize = addEvent(chart, 'resize', blurInputs);
  47360. // Extend the buttonOptions with actual range
  47361. buttonOptions.forEach(rangeSelector.computeButtonRange);
  47362. // zoomed range based on a pre-selected button index
  47363. if (typeof selectedOption !== 'undefined' &&
  47364. buttonOptions[selectedOption]) {
  47365. this.clickButton(selectedOption, false);
  47366. }
  47367. addEvent(chart, 'load', function () {
  47368. // If a data grouping is applied to the current button, release it
  47369. // when extremes change
  47370. if (chart.xAxis && chart.xAxis[0]) {
  47371. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  47372. if (this.max - this.min !==
  47373. chart.fixedRange &&
  47374. e.trigger !== 'rangeSelectorButton' &&
  47375. e.trigger !== 'updatedData' &&
  47376. rangeSelector.forcedDataGrouping &&
  47377. !rangeSelector.frozenStates) {
  47378. this.setDataGrouping(false, false);
  47379. }
  47380. });
  47381. }
  47382. });
  47383. },
  47384. /**
  47385. * Dynamically update the range selector buttons after a new range has been
  47386. * set
  47387. *
  47388. * @private
  47389. * @function Highcharts.RangeSelector#updateButtonStates
  47390. * @return {void}
  47391. */
  47392. updateButtonStates: function () {
  47393. 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
  47394. unionExtremes = (chart.scroller &&
  47395. 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;
  47396. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  47397. var range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], state = 0, disable, select, offsetRange = rangeOptions._offsetMax -
  47398. rangeOptions._offsetMin, isSelected = i === selected,
  47399. // Disable buttons where the range exceeds what is allowed in
  47400. // the current view
  47401. isTooGreatRange = range >
  47402. dataMax - dataMin,
  47403. // Disable buttons where the range is smaller than the minimum
  47404. // range
  47405. isTooSmallRange = range < baseAxis.minRange,
  47406. // Do not select the YTD button if not explicitly told so
  47407. isYTDButNotSelected = false,
  47408. // Disable the All button if we're already showing all
  47409. isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;
  47410. // Months and years have a variable range so we check the extremes
  47411. if ((type === 'month' || type === 'year') &&
  47412. (actualRange + 36e5 >=
  47413. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  47414. (actualRange - 36e5 <=
  47415. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  47416. isSameRange = true;
  47417. }
  47418. else if (type === 'ytd') {
  47419. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  47420. isYTDButNotSelected = !isSelected;
  47421. }
  47422. else if (type === 'all') {
  47423. isSameRange = (baseAxis.max - baseAxis.min >=
  47424. dataMax - dataMin);
  47425. isAllButAlreadyShowingAll = (!isSelected &&
  47426. selectedExists &&
  47427. isSameRange);
  47428. }
  47429. // The new zoom area happens to match the range for a button - mark
  47430. // it selected. This happens when scrolling across an ordinal gap.
  47431. // It can be seen in the intraday demos when selecting 1h and scroll
  47432. // across the night gap.
  47433. disable = (!allButtonsEnabled &&
  47434. (isTooGreatRange ||
  47435. isTooSmallRange ||
  47436. isAllButAlreadyShowingAll ||
  47437. hasNoData));
  47438. select = ((isSelected && isSameRange) ||
  47439. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  47440. (isSelected && rangeSelector.frozenStates));
  47441. if (disable) {
  47442. state = 3;
  47443. }
  47444. else if (select) {
  47445. selectedExists = true; // Only one button can be selected
  47446. state = 2;
  47447. }
  47448. // If state has changed, update the button
  47449. if (button.state !== state) {
  47450. button.setState(state);
  47451. // Reset (#9209)
  47452. if (state === 0 && selected === i) {
  47453. rangeSelector.setSelected(null);
  47454. }
  47455. }
  47456. });
  47457. },
  47458. /**
  47459. * Compute and cache the range for an individual button
  47460. *
  47461. * @private
  47462. * @function Highcharts.RangeSelector#computeButtonRange
  47463. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  47464. * @return {void}
  47465. */
  47466. computeButtonRange: function (rangeOptions) {
  47467. var type = rangeOptions.type, count = rangeOptions.count || 1,
  47468. // these time intervals have a fixed number of milliseconds, as
  47469. // opposed to month, ytd and year
  47470. fixedTimes = {
  47471. millisecond: 1,
  47472. second: 1000,
  47473. minute: 60 * 1000,
  47474. hour: 3600 * 1000,
  47475. day: 24 * 3600 * 1000,
  47476. week: 7 * 24 * 3600 * 1000
  47477. };
  47478. // Store the range on the button object
  47479. if (fixedTimes[type]) {
  47480. rangeOptions._range = fixedTimes[type] * count;
  47481. }
  47482. else if (type === 'month' || type === 'year') {
  47483. rangeOptions._range = {
  47484. month: 30,
  47485. year: 365
  47486. }[type] * 24 * 36e5 * count;
  47487. }
  47488. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  47489. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  47490. rangeOptions._range +=
  47491. rangeOptions._offsetMax - rangeOptions._offsetMin;
  47492. },
  47493. /**
  47494. * Set the internal and displayed value of a HTML input for the dates
  47495. *
  47496. * @private
  47497. * @function Highcharts.RangeSelector#setInputValue
  47498. * @param {string} name
  47499. * @param {number} [inputTime]
  47500. * @return {void}
  47501. */
  47502. setInputValue: function (name, inputTime) {
  47503. var options = this.chart.options.rangeSelector, time = this.chart.time, input = this[name + 'Input'];
  47504. if (defined(inputTime)) {
  47505. input.previousValue = input.HCTime;
  47506. input.HCTime = inputTime;
  47507. }
  47508. input.value = time.dateFormat(options.inputEditDateFormat || '%Y-%m-%d', input.HCTime);
  47509. this[name + 'DateBox'].attr({
  47510. text: time.dateFormat(options.inputDateFormat || '%b %e, %Y', input.HCTime)
  47511. });
  47512. },
  47513. /**
  47514. * @private
  47515. * @function Highcharts.RangeSelector#showInput
  47516. * @param {string} name
  47517. * @return {void}
  47518. */
  47519. showInput: function (name) {
  47520. var inputGroup = this.inputGroup, dateBox = this[name + 'DateBox'];
  47521. css(this[name + 'Input'], {
  47522. left: (inputGroup.translateX + dateBox.x) + 'px',
  47523. top: inputGroup.translateY + 'px',
  47524. width: (dateBox.width - 2) + 'px',
  47525. height: (dateBox.height - 2) + 'px',
  47526. border: '2px solid silver'
  47527. });
  47528. },
  47529. /**
  47530. * @private
  47531. * @function Highcharts.RangeSelector#hideInput
  47532. * @param {string} name
  47533. * @return {void}
  47534. */
  47535. hideInput: function (name) {
  47536. css(this[name + 'Input'], {
  47537. border: 0,
  47538. width: '1px',
  47539. height: '1px'
  47540. });
  47541. this.setInputValue(name);
  47542. },
  47543. /**
  47544. * Draw either the 'from' or the 'to' HTML input box of the range selector
  47545. *
  47546. * @private
  47547. * @function Highcharts.RangeSelector#drawInput
  47548. * @param {string} name
  47549. * @return {void}
  47550. */
  47551. drawInput: function (name) {
  47552. 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;
  47553. /**
  47554. * @private
  47555. */
  47556. function updateExtremes() {
  47557. var inputValue = input.value, value = (options.inputDateParser || Date.parse)(inputValue), chartAxis = chart.xAxis[0], dataAxis = chart.scroller && chart.scroller.xAxis ?
  47558. chart.scroller.xAxis :
  47559. chartAxis, dataMin = dataAxis.dataMin, dataMax = dataAxis.dataMax;
  47560. if (value !== input.previousValue) {
  47561. input.previousValue = value;
  47562. // If the value isn't parsed directly to a value by the
  47563. // browser's Date.parse method, like YYYY-MM-DD in IE, try
  47564. // parsing it a different way
  47565. if (!isNumber(value)) {
  47566. value = inputValue.split('-');
  47567. value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
  47568. }
  47569. if (isNumber(value)) {
  47570. // Correct for timezone offset (#433)
  47571. if (!chart.time.useUTC) {
  47572. value =
  47573. value + new Date().getTimezoneOffset() * 60 * 1000;
  47574. }
  47575. // Validate the extremes. If it goes beyound the data min or
  47576. // max, use the actual data extreme (#2438).
  47577. if (isMin) {
  47578. if (value > rangeSelector.maxInput.HCTime) {
  47579. value = void 0;
  47580. }
  47581. else if (value < dataMin) {
  47582. value = dataMin;
  47583. }
  47584. }
  47585. else {
  47586. if (value < rangeSelector.minInput.HCTime) {
  47587. value = void 0;
  47588. }
  47589. else if (value > dataMax) {
  47590. value = dataMax;
  47591. }
  47592. }
  47593. // Set the extremes
  47594. if (typeof value !== 'undefined') { // @todo typof undefined
  47595. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  47596. }
  47597. }
  47598. }
  47599. }
  47600. // Create the text label
  47601. this[name + 'Label'] = label = renderer
  47602. .label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
  47603. .addClass('highcharts-range-label')
  47604. .attr({
  47605. padding: 2
  47606. })
  47607. .add(inputGroup);
  47608. inputGroup.offset += label.width + 5;
  47609. // Create an SVG label that shows updated date ranges and and records
  47610. // click events that bring in the HTML input.
  47611. this[name + 'DateBox'] = dateBox = renderer
  47612. .label('', inputGroup.offset)
  47613. .addClass('highcharts-range-input')
  47614. .attr({
  47615. padding: 2,
  47616. width: options.inputBoxWidth || 90,
  47617. height: options.inputBoxHeight || 17,
  47618. 'text-align': 'center'
  47619. })
  47620. .on('click', function () {
  47621. // If it is already focused, the onfocus event doesn't fire
  47622. // (#3713)
  47623. rangeSelector.showInput(name);
  47624. rangeSelector[name + 'Input'].focus();
  47625. });
  47626. if (!chart.styledMode) {
  47627. dateBox.attr({
  47628. stroke: options.inputBoxBorderColor || '#cccccc',
  47629. 'stroke-width': 1
  47630. });
  47631. }
  47632. dateBox.add(inputGroup);
  47633. inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
  47634. // Create the HTML input element. This is rendered as 1x1 pixel then set
  47635. // to the right size when focused.
  47636. this[name + 'Input'] = input = createElement('input', {
  47637. name: name,
  47638. className: 'highcharts-range-selector',
  47639. type: 'text'
  47640. }, {
  47641. top: chart.plotTop + 'px' // prevent jump on focus in Firefox
  47642. }, div);
  47643. if (!chart.styledMode) {
  47644. // Styles
  47645. label.css(merge(chartStyle, options.labelStyle));
  47646. dateBox.css(merge({
  47647. color: '#333333'
  47648. }, chartStyle, options.inputStyle));
  47649. css(input, extend({
  47650. position: 'absolute',
  47651. border: 0,
  47652. width: '1px',
  47653. height: '1px',
  47654. padding: 0,
  47655. textAlign: 'center',
  47656. fontSize: chartStyle.fontSize,
  47657. fontFamily: chartStyle.fontFamily,
  47658. top: '-9999em' // #4798
  47659. }, options.inputStyle));
  47660. }
  47661. // Blow up the input box
  47662. input.onfocus = function () {
  47663. rangeSelector.showInput(name);
  47664. };
  47665. // Hide away the input box
  47666. input.onblur = function () {
  47667. // update extermes only when inputs are active
  47668. if (input === H.doc.activeElement) { // Only when focused
  47669. // Update also when no `change` event is triggered, like when
  47670. // clicking inside the SVG (#4710)
  47671. updateExtremes();
  47672. }
  47673. // #10404 - move hide and blur outside focus
  47674. rangeSelector.hideInput(name);
  47675. input.blur(); // #4606
  47676. };
  47677. // handle changes in the input boxes
  47678. input.onchange = updateExtremes;
  47679. input.onkeypress = function (event) {
  47680. // IE does not fire onchange on enter
  47681. if (event.keyCode === 13) {
  47682. updateExtremes();
  47683. }
  47684. };
  47685. },
  47686. /**
  47687. * Get the position of the range selector buttons and inputs. This can be
  47688. * overridden from outside for custom positioning.
  47689. *
  47690. * @private
  47691. * @function Highcharts.RangeSelector#getPosition
  47692. *
  47693. * @return {Highcharts.Dictionary<number>}
  47694. */
  47695. getPosition: function () {
  47696. var chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?
  47697. chart.plotTop - chart.axisOffset[0] :
  47698. 0; // set offset only for varticalAlign top
  47699. return {
  47700. buttonTop: top + options.buttonPosition.y,
  47701. inputTop: top + options.inputPosition.y - 10
  47702. };
  47703. },
  47704. /**
  47705. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  47706. * the current timestamp. Will choose dataMin if its value is higher than
  47707. * the timestamp for the start of current year.
  47708. *
  47709. * @private
  47710. * @function Highcharts.RangeSelector#getYTDExtremes
  47711. *
  47712. * @param {number} dataMax
  47713. *
  47714. * @param {number} dataMin
  47715. *
  47716. * @return {*}
  47717. * Returns min and max for the YTD
  47718. */
  47719. getYTDExtremes: function (dataMax, dataMin, useUTC) {
  47720. var time = this.chart.time, min, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?
  47721. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  47722. +new time.Date(year, 0, 1);
  47723. min = Math.max(dataMin || 0, startOfYear);
  47724. now = now.getTime();
  47725. return {
  47726. max: Math.min(dataMax || now, now),
  47727. min: min
  47728. };
  47729. },
  47730. /**
  47731. * Render the range selector including the buttons and the inputs. The first
  47732. * time render is called, the elements are created and positioned. On
  47733. * subsequent calls, they are moved and updated.
  47734. *
  47735. * @private
  47736. * @function Highcharts.RangeSelector#render
  47737. * @param {number} [min]
  47738. * X axis minimum
  47739. * @param {number} [max]
  47740. * X axis maximum
  47741. * @return {void}
  47742. */
  47743. render: function (min, max) {
  47744. var rangeSelector = this, chart = rangeSelector.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, navButtonOptions = (chartOptions.exporting &&
  47745. chartOptions.exporting.enabled !== false &&
  47746. chartOptions.navigation &&
  47747. chartOptions.navigation.buttonOptions), lang = defaultOptions.lang, div = rangeSelector.div, options = chartOptions.rangeSelector,
  47748. // Place inputs above the container
  47749. inputsZIndex = pick(chartOptions.chart.style &&
  47750. 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 = rendered || false, verb = animate ? 'animate' : 'attr', exportingX = 0, alignTranslateY, legendHeight, minPosition, translateY = 0, translateX;
  47751. if (options.enabled === false) {
  47752. return;
  47753. }
  47754. // create the elements
  47755. if (!rendered) {
  47756. rangeSelector.group = group = renderer.g('range-selector-group')
  47757. .attr({
  47758. zIndex: 7
  47759. })
  47760. .add();
  47761. rangeSelector.buttonGroup = buttonGroup =
  47762. renderer.g('range-selector-buttons').add(group);
  47763. rangeSelector.zoomText = renderer
  47764. .text(lang.rangeSelectorZoom, 0, 15)
  47765. .add(buttonGroup);
  47766. if (!chart.styledMode) {
  47767. rangeSelector.zoomText.css(options.labelStyle);
  47768. buttonTheme['stroke-width'] =
  47769. pick(buttonTheme['stroke-width'], 0);
  47770. }
  47771. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  47772. buttons[i] = renderer
  47773. .button(rangeOptions.text, 0, 0, function (e) {
  47774. // extract events from button object and call
  47775. var buttonEvents = (rangeOptions.events &&
  47776. rangeOptions.events.click), callDefaultEvent;
  47777. if (buttonEvents) {
  47778. callDefaultEvent =
  47779. buttonEvents.call(rangeOptions, e);
  47780. }
  47781. if (callDefaultEvent !== false) {
  47782. rangeSelector.clickButton(i);
  47783. }
  47784. rangeSelector.isActive = true;
  47785. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  47786. .attr({
  47787. 'text-align': 'center'
  47788. })
  47789. .add(buttonGroup);
  47790. });
  47791. // first create a wrapper outside the container in order to make
  47792. // the inputs work and make export correct
  47793. if (inputEnabled !== false) {
  47794. rangeSelector.div = div = createElement('div', null, {
  47795. position: 'relative',
  47796. height: 0,
  47797. zIndex: inputsZIndex
  47798. });
  47799. container.parentNode.insertBefore(div, container);
  47800. // Create the group to keep the inputs
  47801. rangeSelector.inputGroup = inputGroup =
  47802. renderer.g('input-group').add(group);
  47803. inputGroup.offset = 0;
  47804. rangeSelector.drawInput('min');
  47805. rangeSelector.drawInput('max');
  47806. }
  47807. }
  47808. // #8769, allow dynamically updating margins
  47809. rangeSelector.zoomText[verb]({
  47810. x: pick(plotLeft + buttonPosition.x, plotLeft)
  47811. });
  47812. // button start position
  47813. buttonLeft = pick(plotLeft + buttonPosition.x, plotLeft) +
  47814. rangeSelector.zoomText.getBBox().width + 5;
  47815. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  47816. buttons[i][verb]({ x: buttonLeft });
  47817. // increase button position for the next button
  47818. buttonLeft += buttons[i].width + pick(options.buttonSpacing, 5);
  47819. });
  47820. plotLeft = chart.plotLeft - chart.spacing[3];
  47821. rangeSelector.updateButtonStates();
  47822. // detect collisiton with exporting
  47823. if (navButtonOptions &&
  47824. this.titleCollision(chart) &&
  47825. verticalAlign === 'top' &&
  47826. buttonPosition.align === 'right' && ((buttonPosition.y +
  47827. buttonGroup.getBBox().height - 12) <
  47828. ((navButtonOptions.y || 0) +
  47829. navButtonOptions.height))) {
  47830. exportingX = -40;
  47831. }
  47832. if (buttonPosition.align === 'left') {
  47833. translateX = buttonPosition.x - chart.spacing[3];
  47834. }
  47835. else if (buttonPosition.align === 'right') {
  47836. translateX =
  47837. buttonPosition.x + exportingX - chart.spacing[1];
  47838. }
  47839. // align button group
  47840. buttonGroup.align({
  47841. y: buttonPosition.y,
  47842. width: buttonGroup.getBBox().width,
  47843. align: buttonPosition.align,
  47844. x: translateX
  47845. }, true, chart.spacingBox);
  47846. // skip animation
  47847. rangeSelector.group.placed = animate;
  47848. rangeSelector.buttonGroup.placed = animate;
  47849. if (inputEnabled !== false) {
  47850. var inputGroupX, inputGroupWidth, buttonGroupX, buttonGroupWidth;
  47851. // detect collision with exporting
  47852. if (navButtonOptions &&
  47853. this.titleCollision(chart) &&
  47854. verticalAlign === 'top' &&
  47855. inputPosition.align === 'right' && ((inputPosition.y -
  47856. inputGroup.getBBox().height - 12) <
  47857. ((navButtonOptions.y || 0) +
  47858. navButtonOptions.height +
  47859. chart.spacing[0]))) {
  47860. exportingX = -40;
  47861. }
  47862. else {
  47863. exportingX = 0;
  47864. }
  47865. if (inputPosition.align === 'left') {
  47866. translateX = plotLeft;
  47867. }
  47868. else if (inputPosition.align === 'right') {
  47869. translateX = -Math.max(chart.axisOffset[1], -exportingX);
  47870. }
  47871. // Update the alignment to the updated spacing box
  47872. inputGroup.align({
  47873. y: inputPosition.y,
  47874. width: inputGroup.getBBox().width,
  47875. align: inputPosition.align,
  47876. // fix wrong getBBox() value on right align
  47877. x: inputPosition.x + translateX - 2
  47878. }, true, chart.spacingBox);
  47879. // detect collision
  47880. inputGroupX = (inputGroup.alignAttr.translateX +
  47881. inputGroup.alignOptions.x -
  47882. exportingX +
  47883. // getBBox for detecing left margin
  47884. inputGroup.getBBox().x +
  47885. // 2px padding to not overlap input and label
  47886. 2);
  47887. inputGroupWidth = inputGroup.alignOptions.width;
  47888. buttonGroupX = buttonGroup.alignAttr.translateX +
  47889. buttonGroup.getBBox().x;
  47890. // 20 is minimal spacing between elements
  47891. buttonGroupWidth = buttonGroup.getBBox().width + 20;
  47892. if ((inputPosition.align ===
  47893. buttonPosition.align) || ((buttonGroupX + buttonGroupWidth > inputGroupX) &&
  47894. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  47895. (buttonPositionY <
  47896. (inputPositionY +
  47897. inputGroup.getBBox().height)))) {
  47898. inputGroup.attr({
  47899. translateX: inputGroup.alignAttr.translateX +
  47900. (chart.axisOffset[1] >= -exportingX ? 0 : -exportingX),
  47901. translateY: inputGroup.alignAttr.translateY +
  47902. buttonGroup.getBBox().height + 10
  47903. });
  47904. }
  47905. // Set or reset the input values
  47906. rangeSelector.setInputValue('min', min);
  47907. rangeSelector.setInputValue('max', max);
  47908. // skip animation
  47909. rangeSelector.inputGroup.placed = animate;
  47910. }
  47911. // vertical align
  47912. rangeSelector.group.align({
  47913. verticalAlign: verticalAlign
  47914. }, true, chart.spacingBox);
  47915. // set position
  47916. groupHeight =
  47917. rangeSelector.group.getBBox().height + 20; // # 20 padding
  47918. alignTranslateY =
  47919. rangeSelector.group.alignAttr.translateY;
  47920. // calculate bottom position
  47921. if (verticalAlign === 'bottom') {
  47922. legendHeight = (legendOptions &&
  47923. legendOptions.verticalAlign === 'bottom' &&
  47924. legendOptions.enabled &&
  47925. !legendOptions.floating ?
  47926. legend.legendHeight + pick(legendOptions.margin, 10) :
  47927. 0);
  47928. groupHeight = groupHeight + legendHeight - 20;
  47929. translateY = (alignTranslateY -
  47930. groupHeight -
  47931. (floating ? 0 : options.y) -
  47932. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  47933. 10 // 10 spacing
  47934. );
  47935. }
  47936. if (verticalAlign === 'top') {
  47937. if (floating) {
  47938. translateY = 0;
  47939. }
  47940. if (chart.titleOffset && chart.titleOffset[0]) {
  47941. translateY = chart.titleOffset[0];
  47942. }
  47943. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  47944. }
  47945. else if (verticalAlign === 'middle') {
  47946. if (inputPositionY === buttonPositionY) {
  47947. if (inputPositionY < 0) {
  47948. translateY = alignTranslateY + minPosition;
  47949. }
  47950. else {
  47951. translateY = alignTranslateY;
  47952. }
  47953. }
  47954. else if (inputPositionY || buttonPositionY) {
  47955. if (inputPositionY < 0 ||
  47956. buttonPositionY < 0) {
  47957. translateY -= Math.min(inputPositionY, buttonPositionY);
  47958. }
  47959. else {
  47960. translateY =
  47961. alignTranslateY - groupHeight + minPosition;
  47962. }
  47963. }
  47964. }
  47965. rangeSelector.group.translate(options.x, options.y + Math.floor(translateY));
  47966. // translate HTML inputs
  47967. if (inputEnabled !== false) {
  47968. rangeSelector.minInput.style.marginTop =
  47969. rangeSelector.group.translateY + 'px';
  47970. rangeSelector.maxInput.style.marginTop =
  47971. rangeSelector.group.translateY + 'px';
  47972. }
  47973. rangeSelector.rendered = true;
  47974. },
  47975. /**
  47976. * Extracts height of range selector
  47977. *
  47978. * @private
  47979. * @function Highcharts.RangeSelector#getHeight
  47980. * @return {number}
  47981. * Returns rangeSelector height
  47982. */
  47983. getHeight: function () {
  47984. 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;
  47985. if (options.height) {
  47986. return options.height;
  47987. }
  47988. rangeSelectorHeight = rangeSelectorGroup ?
  47989. // 13px to keep back compatibility
  47990. (rangeSelectorGroup.getBBox(true).height) + 13 +
  47991. yPosition :
  47992. 0;
  47993. minPosition = Math.min(inputPositionY, buttonPositionY);
  47994. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  47995. (inputPositionY > 0 && buttonPositionY > 0)) {
  47996. rangeSelectorHeight += Math.abs(minPosition);
  47997. }
  47998. return rangeSelectorHeight;
  47999. },
  48000. /**
  48001. * Detect collision with title or subtitle
  48002. *
  48003. * @private
  48004. * @function Highcharts.RangeSelector#titleCollision
  48005. *
  48006. * @param {Highcharts.Chart} chart
  48007. *
  48008. * @return {boolean}
  48009. * Returns collision status
  48010. */
  48011. titleCollision: function (chart) {
  48012. return !(chart.options.title.text ||
  48013. chart.options.subtitle.text);
  48014. },
  48015. /**
  48016. * Update the range selector with new options
  48017. *
  48018. * @private
  48019. * @function Highcharts.RangeSelector#update
  48020. * @param {Highcharts.RangeSelectorOptions} options
  48021. * @return {void}
  48022. */
  48023. update: function (options) {
  48024. var chart = this.chart;
  48025. merge(true, chart.options.rangeSelector, options);
  48026. this.destroy();
  48027. this.init(chart);
  48028. chart.rangeSelector.render();
  48029. },
  48030. /**
  48031. * Destroys allocated elements.
  48032. *
  48033. * @private
  48034. * @function Highcharts.RangeSelector#destroy
  48035. */
  48036. destroy: function () {
  48037. var rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;
  48038. rSelector.unMouseDown();
  48039. rSelector.unResize();
  48040. // Destroy elements in collections
  48041. destroyObjectProperties(rSelector.buttons);
  48042. // Clear input element events
  48043. if (minInput) {
  48044. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  48045. }
  48046. if (maxInput) {
  48047. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  48048. }
  48049. // Destroy HTML and SVG elements
  48050. objectEach(rSelector, function (val, key) {
  48051. if (val && key !== 'chart') {
  48052. if (val.destroy) {
  48053. // SVGElement
  48054. val.destroy();
  48055. }
  48056. else if (val.nodeType) {
  48057. // HTML element
  48058. discardElement(this[key]);
  48059. }
  48060. }
  48061. if (val !== RangeSelector.prototype[key]) {
  48062. rSelector[key] = null;
  48063. }
  48064. }, this);
  48065. }
  48066. };
  48067. /**
  48068. * Get the axis min value based on the range option and the current max. For
  48069. * stock charts this is extended via the {@link RangeSelector} so that if the
  48070. * selected range is a multiple of months or years, it is compensated for
  48071. * various month lengths.
  48072. *
  48073. * @private
  48074. * @function Highcharts.Axis#minFromRange
  48075. * @return {number|undefined}
  48076. * The new minimum value.
  48077. */
  48078. Axis.prototype.minFromRange = function () {
  48079. var rangeOptions = this.range, type = rangeOptions.type, timeName = {
  48080. month: 'Month',
  48081. year: 'FullYear'
  48082. }[type], min, max = this.max, dataMin, range, time = this.chart.time,
  48083. // Get the true range from a start date
  48084. getTrueRange = function (base, count) {
  48085. var date = new time.Date(base), basePeriod = time.get(timeName, date);
  48086. time.set(timeName, date, basePeriod + count);
  48087. if (basePeriod === time.get(timeName, date)) {
  48088. time.set('Date', date, 0); // #6537
  48089. }
  48090. return date.getTime() - base;
  48091. };
  48092. if (isNumber(rangeOptions)) {
  48093. min = max - rangeOptions;
  48094. range = rangeOptions;
  48095. }
  48096. else {
  48097. min = max + getTrueRange(max, -rangeOptions.count);
  48098. // Let the fixedRange reflect initial settings (#5930)
  48099. if (this.chart) {
  48100. this.chart.fixedRange = max - min;
  48101. }
  48102. }
  48103. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  48104. if (!isNumber(min)) {
  48105. min = dataMin;
  48106. }
  48107. if (min <= dataMin) {
  48108. min = dataMin;
  48109. if (typeof range === 'undefined') { // #4501
  48110. range = getTrueRange(min, rangeOptions.count);
  48111. }
  48112. this.newMax = Math.min(min + range, this.dataMax);
  48113. }
  48114. if (!isNumber(max)) {
  48115. min = void 0;
  48116. }
  48117. return min;
  48118. };
  48119. if (!H.RangeSelector) {
  48120. // Initialize rangeselector for stock charts
  48121. addEvent(Chart, 'afterGetContainer', function () {
  48122. if (this.options.rangeSelector.enabled) {
  48123. this.rangeSelector = new RangeSelector(this);
  48124. }
  48125. });
  48126. addEvent(Chart, 'beforeRender', function () {
  48127. var chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector, verticalAlign;
  48128. if (rangeSelector) {
  48129. if (isNumber(rangeSelector.deferredYTDClick)) {
  48130. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  48131. delete rangeSelector.deferredYTDClick;
  48132. }
  48133. axes.forEach(function (axis) {
  48134. axis.updateNames();
  48135. axis.setScale();
  48136. });
  48137. chart.getAxisMargins();
  48138. rangeSelector.render();
  48139. verticalAlign = rangeSelector.options.verticalAlign;
  48140. if (!rangeSelector.options.floating) {
  48141. if (verticalAlign === 'bottom') {
  48142. this.extraBottomMargin = true;
  48143. }
  48144. else if (verticalAlign !== 'middle') {
  48145. this.extraTopMargin = true;
  48146. }
  48147. }
  48148. }
  48149. });
  48150. addEvent(Chart, 'update', function (e) {
  48151. var chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, rangeSelector = chart.rangeSelector, verticalAlign, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;
  48152. if (optionsRangeSelector &&
  48153. optionsRangeSelector.enabled &&
  48154. !defined(rangeSelector)) {
  48155. this.options.rangeSelector.enabled = true;
  48156. this.rangeSelector = new RangeSelector(this);
  48157. }
  48158. this.extraBottomMargin = false;
  48159. this.extraTopMargin = false;
  48160. if (rangeSelector) {
  48161. rangeSelector.render();
  48162. verticalAlign = (optionsRangeSelector &&
  48163. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  48164. if (!rangeSelector.options.floating) {
  48165. if (verticalAlign === 'bottom') {
  48166. this.extraBottomMargin = true;
  48167. }
  48168. else if (verticalAlign !== 'middle') {
  48169. this.extraTopMargin = true;
  48170. }
  48171. }
  48172. if (this.extraBottomMargin !== extraBottomMarginWas ||
  48173. this.extraTopMargin !== extraTopMarginWas) {
  48174. this.isDirtyBox = true;
  48175. }
  48176. }
  48177. });
  48178. addEvent(Chart, 'render', function () {
  48179. var chart = this, rangeSelector = chart.rangeSelector, verticalAlign;
  48180. if (rangeSelector && !rangeSelector.options.floating) {
  48181. rangeSelector.render();
  48182. verticalAlign = rangeSelector.options.verticalAlign;
  48183. if (verticalAlign === 'bottom') {
  48184. this.extraBottomMargin = true;
  48185. }
  48186. else if (verticalAlign !== 'middle') {
  48187. this.extraTopMargin = true;
  48188. }
  48189. }
  48190. });
  48191. addEvent(Chart, 'getMargins', function () {
  48192. var rangeSelector = this.rangeSelector, rangeSelectorHeight;
  48193. if (rangeSelector) {
  48194. rangeSelectorHeight = rangeSelector.getHeight();
  48195. if (this.extraTopMargin) {
  48196. this.plotTop += rangeSelectorHeight;
  48197. }
  48198. if (this.extraBottomMargin) {
  48199. this.marginBottom += rangeSelectorHeight;
  48200. }
  48201. }
  48202. });
  48203. Chart.prototype.callbacks.push(function (chart) {
  48204. var extremes, rangeSelector = chart.rangeSelector, unbindRender, unbindSetExtremes;
  48205. /**
  48206. * @private
  48207. */
  48208. function renderRangeSelector() {
  48209. extremes = chart.xAxis[0].getExtremes();
  48210. if (isNumber(extremes.min)) {
  48211. rangeSelector.render(extremes.min, extremes.max);
  48212. }
  48213. }
  48214. if (rangeSelector) {
  48215. // redraw the scroller on setExtremes
  48216. unbindSetExtremes = addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  48217. rangeSelector.render(e.min, e.max);
  48218. });
  48219. // redraw the scroller chart resize
  48220. unbindRender = addEvent(chart, 'redraw', renderRangeSelector);
  48221. // do it now
  48222. renderRangeSelector();
  48223. }
  48224. // Remove resize/afterSetExtremes at chart destroy
  48225. addEvent(chart, 'destroy', function destroyEvents() {
  48226. if (rangeSelector) {
  48227. unbindRender();
  48228. unbindSetExtremes();
  48229. }
  48230. });
  48231. });
  48232. H.RangeSelector = RangeSelector;
  48233. }
  48234. });
  48235. _registerModule(_modules, 'parts/StockChart.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  48236. /* *
  48237. *
  48238. * (c) 2010-2019 Torstein Honsi
  48239. *
  48240. * License: www.highcharts.com/license
  48241. *
  48242. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48243. *
  48244. * */
  48245. var arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, defined = U.defined, extend = U.extend, isNumber = U.isNumber, isString = U.isString, pick = U.pick, splat = U.splat;
  48246. // Has a dependency on Navigator due to the use of
  48247. // defaultOptions.navigator
  48248. // Has a dependency on Scrollbar due to the use of
  48249. // defaultOptions.scrollbar
  48250. // Has a dependency on RangeSelector due to the use of
  48251. // defaultOptions.rangeSelector
  48252. var addEvent = H.addEvent, Axis = H.Axis, Chart = H.Chart, format = H.format, merge = H.merge, Point = H.Point, Renderer = H.Renderer, Series = H.Series, SVGRenderer = H.SVGRenderer, VMLRenderer = H.VMLRenderer, seriesProto = Series.prototype, seriesInit = seriesProto.init, seriesProcessData = seriesProto.processData, pointTooltipFormatter = Point.prototype.tooltipFormatter;
  48253. /**
  48254. * Compare the values of the series against the first non-null, non-
  48255. * zero value in the visible range. The y axis will show percentage
  48256. * or absolute change depending on whether `compare` is set to `"percent"`
  48257. * or `"value"`. When this is applied to multiple series, it allows
  48258. * comparing the development of the series against each other. Adds
  48259. * a `change` field to every point object.
  48260. *
  48261. * @see [compareBase](#plotOptions.series.compareBase)
  48262. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  48263. *
  48264. * @sample {highstock} stock/plotoptions/series-compare-percent/
  48265. * Percent
  48266. * @sample {highstock} stock/plotoptions/series-compare-value/
  48267. * Value
  48268. *
  48269. * @type {string}
  48270. * @since 1.0.1
  48271. * @product highstock
  48272. * @apioption plotOptions.series.compare
  48273. */
  48274. /**
  48275. * Defines if comparison should start from the first point within the visible
  48276. * range or should start from the first point **before** the range.
  48277. *
  48278. * In other words, this flag determines if first point within the visible range
  48279. * will have 0% (`compareStart=true`) or should have been already calculated
  48280. * according to the previous point (`compareStart=false`).
  48281. *
  48282. * @sample {highstock} stock/plotoptions/series-comparestart/
  48283. * Calculate compare within visible range
  48284. *
  48285. * @type {boolean}
  48286. * @default false
  48287. * @since 6.0.0
  48288. * @product highstock
  48289. * @apioption plotOptions.series.compareStart
  48290. */
  48291. /**
  48292. * When [compare](#plotOptions.series.compare) is `percent`, this option
  48293. * dictates whether to use 0 or 100 as the base of comparison.
  48294. *
  48295. * @sample {highstock} stock/plotoptions/series-comparebase/
  48296. * Compare base is 100
  48297. *
  48298. * @type {number}
  48299. * @default 0
  48300. * @since 5.0.6
  48301. * @product highstock
  48302. * @validvalue [0, 100]
  48303. * @apioption plotOptions.series.compareBase
  48304. */
  48305. /* eslint-disable no-invalid-this, valid-jsdoc */
  48306. /**
  48307. * Factory function for creating new stock charts. Creates a new
  48308. * {@link Highcharts.Chart|Chart} object with different default options than the
  48309. * basic Chart.
  48310. *
  48311. * @example
  48312. * var chart = Highcharts.stockChart('container', {
  48313. * series: [{
  48314. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  48315. * pointInterval: 24 * 60 * 60 * 1000
  48316. * }]
  48317. * });
  48318. *
  48319. * @function Highcharts.stockChart
  48320. *
  48321. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  48322. * The DOM element to render to, or its id.
  48323. *
  48324. * @param {Highcharts.Options} options
  48325. * The chart options structure as described in the
  48326. * [options reference](https://api.highcharts.com/highstock).
  48327. *
  48328. * @param {Highcharts.ChartCallbackFunction} [callback]
  48329. * A function to execute when the chart object is finished loading and
  48330. * rendering. In most cases the chart is built in one thread, but in
  48331. * Internet Explorer version 8 or less the chart is sometimes
  48332. * initialized before the document is ready, and in these cases the
  48333. * chart object will not be finished synchronously. As a consequence,
  48334. * code that relies on the newly built Chart object should always run in
  48335. * the callback. Defining a
  48336. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  48337. * handler is equivalent.
  48338. *
  48339. * @return {Highcharts.Chart}
  48340. * The chart object.
  48341. */
  48342. H.StockChart = H.stockChart = function (a, b, c) {
  48343. var hasRenderToArg = isString(a) || a.nodeName, options = arguments[hasRenderToArg ? 1 : 0], userOptions = options,
  48344. // to increase performance, don't merge the data
  48345. seriesOptions = options.series, defaultOptions = H.getOptions(), opposite, panning = options.chart && options.chart.panning,
  48346. // Always disable startOnTick:true on the main axis when the navigator
  48347. // is enabled (#1090)
  48348. navigatorEnabled = pick(options.navigator && options.navigator.enabled, defaultOptions.navigator.enabled, true), verticalPanningEnabled = panning && /y/.test(panning.type), disableStartOnTick = {
  48349. startOnTick: false,
  48350. endOnTick: false
  48351. };
  48352. // apply X axis options to both single and multi y axes
  48353. options.xAxis = splat(options.xAxis || {}).map(function (xAxisOptions, i) {
  48354. return merge({
  48355. minPadding: 0,
  48356. maxPadding: 0,
  48357. overscroll: 0,
  48358. ordinal: true,
  48359. title: {
  48360. text: null
  48361. },
  48362. labels: {
  48363. overflow: 'justify'
  48364. },
  48365. showLastLabel: true
  48366. }, defaultOptions.xAxis, // #3802
  48367. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  48368. xAxisOptions, // user options
  48369. {
  48370. type: 'datetime',
  48371. categories: null
  48372. }, (navigatorEnabled ? disableStartOnTick : null));
  48373. });
  48374. // apply Y axis options to both single and multi y axes
  48375. options.yAxis = splat(options.yAxis || {}).map(function (yAxisOptions, i) {
  48376. opposite = pick(yAxisOptions.opposite, true);
  48377. return merge({
  48378. labels: {
  48379. y: -2
  48380. },
  48381. opposite: opposite,
  48382. /**
  48383. * @default {highcharts} true
  48384. * @default {highstock} false
  48385. * @apioption yAxis.showLastLabel
  48386. *
  48387. * @private
  48388. */
  48389. showLastLabel: !!(
  48390. // #6104, show last label by default for category axes
  48391. yAxisOptions.categories ||
  48392. yAxisOptions.type === 'category'),
  48393. title: {
  48394. text: null
  48395. }
  48396. }, defaultOptions.yAxis, // #3802
  48397. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  48398. yAxisOptions, // user options
  48399. (verticalPanningEnabled ? disableStartOnTick : null));
  48400. });
  48401. options.series = null;
  48402. options = merge({
  48403. chart: {
  48404. panning: {
  48405. enabled: true,
  48406. type: 'x'
  48407. },
  48408. pinchType: 'x'
  48409. },
  48410. navigator: {
  48411. enabled: navigatorEnabled
  48412. },
  48413. scrollbar: {
  48414. // #4988 - check if setOptions was called
  48415. enabled: pick(defaultOptions.scrollbar.enabled, true)
  48416. },
  48417. rangeSelector: {
  48418. // #4988 - check if setOptions was called
  48419. enabled: pick(defaultOptions.rangeSelector.enabled, true)
  48420. },
  48421. title: {
  48422. text: null
  48423. },
  48424. tooltip: {
  48425. split: pick(defaultOptions.tooltip.split, true),
  48426. crosshairs: true
  48427. },
  48428. legend: {
  48429. enabled: false
  48430. }
  48431. }, options, // user's options
  48432. {
  48433. isStock: true // internal flag
  48434. });
  48435. options.series = userOptions.series = seriesOptions;
  48436. return hasRenderToArg ?
  48437. new Chart(a, options, c) :
  48438. new Chart(options, b);
  48439. };
  48440. // Handle som Stock-specific series defaults, override the plotOptions before
  48441. // series options are handled.
  48442. addEvent(Series, 'setOptions', function (e) {
  48443. var series = this, overrides;
  48444. /**
  48445. * @private
  48446. */
  48447. function is(type) {
  48448. return H.seriesTypes[type] && series instanceof H.seriesTypes[type];
  48449. }
  48450. if (this.chart.options.isStock) {
  48451. if (is('column') || is('columnrange')) {
  48452. overrides = {
  48453. borderWidth: 0,
  48454. shadow: false
  48455. };
  48456. }
  48457. else if (is('line') && !is('scatter') && !is('sma')) {
  48458. overrides = {
  48459. marker: {
  48460. enabled: false,
  48461. radius: 2
  48462. }
  48463. };
  48464. }
  48465. if (overrides) {
  48466. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  48467. }
  48468. }
  48469. });
  48470. // Override the automatic label alignment so that the first Y axis' labels
  48471. // are drawn on top of the grid line, and subsequent axes are drawn outside
  48472. addEvent(Axis, 'autoLabelAlign', function (e) {
  48473. var chart = this.chart, options = this.options, panes = chart._labelPanes = chart._labelPanes || {}, key, labelOptions = this.options.labels;
  48474. if (this.chart.options.isStock && this.coll === 'yAxis') {
  48475. key = options.top + ',' + options.height;
  48476. // do it only for the first Y axis of each pane
  48477. if (!panes[key] && labelOptions.enabled) {
  48478. if (labelOptions.x === 15) { // default
  48479. labelOptions.x = 0;
  48480. }
  48481. if (typeof labelOptions.align === 'undefined') {
  48482. labelOptions.align = 'right';
  48483. }
  48484. panes[key] = this;
  48485. e.align = 'right';
  48486. e.preventDefault();
  48487. }
  48488. }
  48489. });
  48490. // Clear axis from label panes (#6071)
  48491. addEvent(Axis, 'destroy', function () {
  48492. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  48493. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  48494. delete chart._labelPanes[key];
  48495. }
  48496. });
  48497. // Override getPlotLinePath to allow for multipane charts
  48498. addEvent(Axis, 'getPlotLinePath', function (e) {
  48499. var axis = this, series = (this.isLinked && !this.series ?
  48500. this.linkedParent.series :
  48501. 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
  48502. axes2, uniqueAxes, translatedValue = e.translatedValue, value = e.value, force = e.force, transVal;
  48503. /**
  48504. * Return the other axis based on either the axis option or on related
  48505. * series.
  48506. * @private
  48507. */
  48508. function getAxis(coll) {
  48509. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis', opt = axis.options[otherColl];
  48510. // Other axis indexed by number
  48511. if (isNumber(opt)) {
  48512. return [chart[otherColl][opt]];
  48513. }
  48514. // Other axis indexed by id (like navigator)
  48515. if (isString(opt)) {
  48516. return [chart.get(opt)];
  48517. }
  48518. // Auto detect based on existing series
  48519. return series.map(function (s) {
  48520. return s[otherColl];
  48521. });
  48522. }
  48523. if ( // For stock chart, by default render paths across the panes
  48524. // except the case when `acrossPanes` is disabled by user (#6644)
  48525. (chart.options.isStock && e.acrossPanes !== false) &&
  48526. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  48527. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  48528. e.preventDefault();
  48529. // Get the related axes based on series
  48530. axes = getAxis(axis.coll);
  48531. // Get the related axes based options.*Axis setting #2810
  48532. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  48533. axes2.forEach(function (A) {
  48534. if (defined(A.options.id) ?
  48535. A.options.id.indexOf('navigator') === -1 :
  48536. true) {
  48537. var a = (A.isXAxis ? 'yAxis' : 'xAxis'), rax = (defined(A.options[a]) ?
  48538. chart[a][A.options[a]] :
  48539. chart[a][0]);
  48540. if (axis === rax) {
  48541. axes.push(A);
  48542. }
  48543. }
  48544. });
  48545. // Remove duplicates in the axes array. If there are no axes in the axes
  48546. // array, we are adding an axis without data, so we need to populate
  48547. // this with grid lines (#2796).
  48548. uniqueAxes = axes.length ?
  48549. [] :
  48550. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  48551. axes.forEach(function (axis2) {
  48552. if (uniqueAxes.indexOf(axis2) === -1 &&
  48553. // Do not draw on axis which overlap completely. #5424
  48554. !H.find(uniqueAxes, function (unique) {
  48555. return unique.pos === axis2.pos && unique.len === axis2.len;
  48556. })) {
  48557. uniqueAxes.push(axis2);
  48558. }
  48559. });
  48560. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  48561. if (isNumber(transVal)) {
  48562. if (axis.horiz) {
  48563. uniqueAxes.forEach(function (axis2) {
  48564. var skip;
  48565. y1 = axis2.pos;
  48566. y2 = y1 + axis2.len;
  48567. x1 = x2 = Math.round(transVal + axis.transB);
  48568. // outside plot area
  48569. if (force !== 'pass' &&
  48570. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  48571. if (force) {
  48572. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  48573. }
  48574. else {
  48575. skip = true;
  48576. }
  48577. }
  48578. if (!skip) {
  48579. result.push('M', x1, y1, 'L', x2, y2);
  48580. }
  48581. });
  48582. }
  48583. else {
  48584. uniqueAxes.forEach(function (axis2) {
  48585. var skip;
  48586. x1 = axis2.pos;
  48587. x2 = x1 + axis2.len;
  48588. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  48589. // outside plot area
  48590. if (force !== 'pass' &&
  48591. (y1 < axisTop || y1 > axisTop + axis.height)) {
  48592. if (force) {
  48593. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  48594. }
  48595. else {
  48596. skip = true;
  48597. }
  48598. }
  48599. if (!skip) {
  48600. result.push('M', x1, y1, 'L', x2, y2);
  48601. }
  48602. });
  48603. }
  48604. }
  48605. e.path = result.length > 0 ?
  48606. renderer.crispPolyLine(result, e.lineWidth || 1) :
  48607. // #3557 getPlotLinePath in regular Highcharts also returns null
  48608. null;
  48609. }
  48610. });
  48611. /**
  48612. * Function to crisp a line with multiple segments
  48613. *
  48614. * @private
  48615. * @function Highcharts.SVGRenderer#crispPolyLine
  48616. * @param {Highcharts.SVGPathArray} points
  48617. * @param {number} width
  48618. * @return {Highcharts.SVGPathArray}
  48619. */
  48620. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  48621. // points format: ['M', 0, 0, 'L', 100, 0]
  48622. // normalize to a crisp line
  48623. var i;
  48624. for (i = 0; i < points.length; i = i + 6) {
  48625. if (points[i + 1] === points[i + 4]) {
  48626. // Substract due to #1129. Now bottom and left axis gridlines behave
  48627. // the same.
  48628. points[i + 1] = points[i + 4] =
  48629. Math.round(points[i + 1]) - (width % 2 / 2);
  48630. }
  48631. if (points[i + 2] === points[i + 5]) {
  48632. points[i + 2] = points[i + 5] =
  48633. Math.round(points[i + 2]) + (width % 2 / 2);
  48634. }
  48635. }
  48636. return points;
  48637. };
  48638. if (Renderer === VMLRenderer) {
  48639. VMLRenderer.prototype.crispPolyLine = SVGRenderer.prototype.crispPolyLine;
  48640. }
  48641. // Wrapper to hide the label
  48642. addEvent(Axis, 'afterHideCrosshair', function () {
  48643. if (this.crossLabel) {
  48644. this.crossLabel = this.crossLabel.hide();
  48645. }
  48646. });
  48647. // Extend crosshairs to also draw the label
  48648. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  48649. // Check if the label has to be drawn
  48650. if (!defined(this.crosshair.label) ||
  48651. !this.crosshair.label.enabled ||
  48652. !this.cross) {
  48653. return;
  48654. }
  48655. var chart = this.chart, options = this.options.crosshair.label, // the label's options
  48656. horiz = this.horiz, // axis orientation
  48657. opposite = this.opposite, // axis position
  48658. left = this.left, // left position
  48659. top = this.top, // top position
  48660. crossLabel = this.crossLabel, // the svgElement
  48661. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, value, offset = 0,
  48662. // Use last available event (#5287)
  48663. e = event.e || (this.cross && this.cross.e), point = event.point, lin2log = this.lin2log, min, max;
  48664. if (this.isLog) {
  48665. min = lin2log(this.min);
  48666. max = lin2log(this.max);
  48667. }
  48668. else {
  48669. min = this.min;
  48670. max = this.max;
  48671. }
  48672. align = (horiz ? 'center' : opposite ?
  48673. (this.labelAlign === 'right' ? 'right' : 'left') :
  48674. (this.labelAlign === 'left' ? 'left' : 'center'));
  48675. // If the label does not exist yet, create it.
  48676. if (!crossLabel) {
  48677. crossLabel = this.crossLabel = chart.renderer
  48678. .label(null, null, null, options.shape || 'callout')
  48679. .addClass('highcharts-crosshair-label' + (this.series[0] &&
  48680. ' highcharts-color-' + this.series[0].colorIndex))
  48681. .attr({
  48682. align: options.align || align,
  48683. padding: pick(options.padding, 8),
  48684. r: pick(options.borderRadius, 3),
  48685. zIndex: 2
  48686. })
  48687. .add(this.labelGroup);
  48688. // Presentational
  48689. if (!chart.styledMode) {
  48690. crossLabel
  48691. .attr({
  48692. fill: options.backgroundColor ||
  48693. (this.series[0] && this.series[0].color) ||
  48694. '#666666',
  48695. stroke: options.borderColor || '',
  48696. 'stroke-width': options.borderWidth || 0
  48697. })
  48698. .css(extend({
  48699. color: '#ffffff',
  48700. fontWeight: 'normal',
  48701. fontSize: '11px',
  48702. textAlign: 'center'
  48703. }, options.style));
  48704. }
  48705. }
  48706. if (horiz) {
  48707. posx = snap ? point.plotX + left : e.chartX;
  48708. posy = top + (opposite ? 0 : this.height);
  48709. }
  48710. else {
  48711. posx = opposite ? this.width + left : 0;
  48712. posy = snap ? point.plotY + top : e.chartY;
  48713. }
  48714. if (!formatOption && !options.formatter) {
  48715. if (this.isDatetimeAxis) {
  48716. formatFormat = '%b %d, %Y';
  48717. }
  48718. formatOption =
  48719. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  48720. }
  48721. // Show the label
  48722. value = snap ?
  48723. point[this.isXAxis ? 'x' : 'y'] :
  48724. this.toValue(horiz ? e.chartX : e.chartY);
  48725. crossLabel.attr({
  48726. text: formatOption ?
  48727. format(formatOption, { value: value }, chart) :
  48728. options.formatter.call(this, value),
  48729. x: posx,
  48730. y: posy,
  48731. // Crosshair should be rendered within Axis range (#7219)
  48732. visibility: value < min || value > max ?
  48733. 'hidden' :
  48734. 'visible'
  48735. });
  48736. crossBox = crossLabel.getBBox();
  48737. // now it is placed we can correct its position
  48738. if (horiz) {
  48739. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  48740. posy = crossLabel.y - crossBox.height;
  48741. }
  48742. }
  48743. else {
  48744. posy = crossLabel.y - (crossBox.height / 2);
  48745. }
  48746. // check the edges
  48747. if (horiz) {
  48748. limit = {
  48749. left: left - crossBox.x,
  48750. right: left + this.width - crossBox.x
  48751. };
  48752. }
  48753. else {
  48754. limit = {
  48755. left: this.labelAlign === 'left' ? left : 0,
  48756. right: this.labelAlign === 'right' ?
  48757. left + this.width :
  48758. chart.chartWidth
  48759. };
  48760. }
  48761. // left edge
  48762. if (crossLabel.translateX < limit.left) {
  48763. offset = limit.left - crossLabel.translateX;
  48764. }
  48765. // right edge
  48766. if (crossLabel.translateX + crossBox.width >= limit.right) {
  48767. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  48768. }
  48769. // show the crosslabel
  48770. crossLabel.attr({
  48771. x: posx + offset,
  48772. y: posy,
  48773. // First set x and y, then anchorX and anchorY, when box is actually
  48774. // calculated, #5702
  48775. anchorX: horiz ?
  48776. posx :
  48777. (this.opposite ? 0 : chart.chartWidth),
  48778. anchorY: horiz ?
  48779. (this.opposite ? chart.chartHeight : 0) :
  48780. posy + crossBox.height / 2
  48781. });
  48782. });
  48783. /* ************************************************************************** *
  48784. * Start value compare logic *
  48785. * ************************************************************************** */
  48786. /**
  48787. * Extend series.init by adding a method to modify the y value used for plotting
  48788. * on the y axis. This method is called both from the axis when finding dataMin
  48789. * and dataMax, and from the series.translate method.
  48790. *
  48791. * @ignore
  48792. * @function Highcharts.Series#init
  48793. */
  48794. seriesProto.init = function () {
  48795. // Call base method
  48796. seriesInit.apply(this, arguments);
  48797. // Set comparison mode
  48798. this.setCompare(this.options.compare);
  48799. };
  48800. /**
  48801. * Highstock only. Set the
  48802. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  48803. * mode of the series after render time. In most cases it is more useful running
  48804. * {@link Axis#setCompare} on the X axis to update all its series.
  48805. *
  48806. * @function Highcharts.Series#setCompare
  48807. *
  48808. * @param {string} [compare]
  48809. * Can be one of `null` (default), `"percent"` or `"value"`.
  48810. */
  48811. seriesProto.setCompare = function (compare) {
  48812. // Set or unset the modifyValue method
  48813. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  48814. function (value, point) {
  48815. var compareValue = this.compareValue;
  48816. if (typeof value !== 'undefined' &&
  48817. typeof compareValue !== 'undefined') { // #2601, #5814
  48818. // Get the modified value
  48819. if (compare === 'value') {
  48820. value -= compareValue;
  48821. // Compare percent
  48822. }
  48823. else {
  48824. value = 100 * (value / compareValue) -
  48825. (this.options.compareBase === 100 ? 0 : 100);
  48826. }
  48827. // record for tooltip etc.
  48828. if (point) {
  48829. point.change = value;
  48830. }
  48831. return value;
  48832. }
  48833. return 0;
  48834. } :
  48835. null;
  48836. // Survive to export, #5485
  48837. this.userOptions.compare = compare;
  48838. // Mark dirty
  48839. if (this.chart.hasRendered) {
  48840. this.isDirty = true;
  48841. }
  48842. };
  48843. /**
  48844. * Extend series.processData by finding the first y value in the plot area,
  48845. * used for comparing the following values
  48846. *
  48847. * @ignore
  48848. * @function Highcharts.Series#processData
  48849. */
  48850. seriesProto.processData = function (force) {
  48851. var series = this, i, keyIndex = -1, processedXData, processedYData, compareStart = series.options.compareStart === true ? 0 : 1, length, compareValue;
  48852. // call base method
  48853. seriesProcessData.apply(this, arguments);
  48854. if (series.xAxis && series.processedYData) { // not pies
  48855. // local variables
  48856. processedXData = series.processedXData;
  48857. processedYData = series.processedYData;
  48858. length = processedYData.length;
  48859. // For series with more than one value (range, OHLC etc), compare
  48860. // against close or the pointValKey (#4922, #3112, #9854)
  48861. if (series.pointArrayMap) {
  48862. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  48863. }
  48864. // find the first value for comparison
  48865. for (i = 0; i < length - compareStart; i++) {
  48866. compareValue = processedYData[i] && keyIndex > -1 ?
  48867. processedYData[i][keyIndex] :
  48868. processedYData[i];
  48869. if (isNumber(compareValue) &&
  48870. processedXData[i + compareStart] >=
  48871. series.xAxis.min &&
  48872. compareValue !== 0) {
  48873. series.compareValue = compareValue;
  48874. break;
  48875. }
  48876. }
  48877. }
  48878. return;
  48879. };
  48880. // Modify series extremes
  48881. addEvent(Series, 'afterGetExtremes', function () {
  48882. if (this.modifyValue) {
  48883. var extremes = [
  48884. this.modifyValue(this.dataMin),
  48885. this.modifyValue(this.dataMax)
  48886. ];
  48887. this.dataMin = arrayMin(extremes);
  48888. this.dataMax = arrayMax(extremes);
  48889. }
  48890. });
  48891. /**
  48892. * Highstock only. Set the compare mode on all series belonging to an Y axis
  48893. * after render time.
  48894. *
  48895. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  48896. *
  48897. * @sample stock/members/axis-setcompare/
  48898. * Set compoare
  48899. *
  48900. * @function Highcharts.Axis#setCompare
  48901. *
  48902. * @param {string} [compare]
  48903. * The compare mode. Can be one of `null` (default), `"value"` or
  48904. * `"percent"`.
  48905. *
  48906. * @param {boolean} [redraw=true]
  48907. * Whether to redraw the chart or to wait for a later call to
  48908. * {@link Chart#redraw}.
  48909. */
  48910. Axis.prototype.setCompare = function (compare, redraw) {
  48911. if (!this.isXAxis) {
  48912. this.series.forEach(function (series) {
  48913. series.setCompare(compare);
  48914. });
  48915. if (pick(redraw, true)) {
  48916. this.chart.redraw();
  48917. }
  48918. }
  48919. };
  48920. /**
  48921. * Extend the tooltip formatter by adding support for the point.change variable
  48922. * as well as the changeDecimals option.
  48923. *
  48924. * @ignore
  48925. * @function Highcharts.Point#tooltipFormatter
  48926. *
  48927. * @param {string} pointFormat
  48928. */
  48929. Point.prototype.tooltipFormatter = function (pointFormat) {
  48930. var point = this;
  48931. var numberFormatter = point.series.chart.numberFormatter;
  48932. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  48933. return pointTooltipFormatter.apply(this, [pointFormat]);
  48934. };
  48935. /* ************************************************************************** *
  48936. * End value compare logic *
  48937. * ************************************************************************** */
  48938. // Extend the Series prototype to create a separate series clip box. This is
  48939. // related to using multiple panes, and a future pane logic should incorporate
  48940. // this feature (#2754).
  48941. addEvent(Series, 'render', function () {
  48942. var chart = this.chart, clipHeight;
  48943. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  48944. // if the series type handles clipping in the animate method (#2975).
  48945. if (!(chart.is3d && chart.is3d()) &&
  48946. !chart.polar &&
  48947. this.xAxis &&
  48948. !this.xAxis.isRadial // Gauge, #6192
  48949. ) {
  48950. clipHeight = this.yAxis.len;
  48951. // Include xAxis line width (#8031) but only if the Y axis ends on the
  48952. // edge of the X axis (#11005).
  48953. if (this.xAxis.axisLine) {
  48954. var dist = chart.plotTop + chart.plotHeight -
  48955. this.yAxis.pos - this.yAxis.len, lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  48956. if (dist >= 0) {
  48957. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  48958. }
  48959. }
  48960. // First render, initial clip box
  48961. if (!this.clipBox && this.animate) {
  48962. this.clipBox = merge(chart.clipBox);
  48963. this.clipBox.width = this.xAxis.len;
  48964. this.clipBox.height = clipHeight;
  48965. // On redrawing, resizing etc, update the clip rectangle
  48966. }
  48967. else if (chart[this.sharedClipKey]) {
  48968. // animate in case resize is done during initial animation
  48969. chart[this.sharedClipKey].animate({
  48970. width: this.xAxis.len,
  48971. height: clipHeight
  48972. });
  48973. // also change markers clip animation for consistency
  48974. // (marker clip rects should exist only on chart init)
  48975. if (chart[this.sharedClipKey + 'm']) {
  48976. chart[this.sharedClipKey + 'm'].animate({
  48977. width: this.xAxis.len
  48978. });
  48979. }
  48980. }
  48981. }
  48982. });
  48983. addEvent(Chart, 'update', function (e) {
  48984. var options = e.options;
  48985. // Use case: enabling scrollbar from a disabled state.
  48986. // Scrollbar needs to be initialized from a controller, Navigator in this
  48987. // case (#6615)
  48988. if ('scrollbar' in options && this.navigator) {
  48989. merge(true, this.options.scrollbar, options.scrollbar);
  48990. this.navigator.update({}, false);
  48991. delete options.scrollbar;
  48992. }
  48993. });
  48994. // Extend the Axis prototype to calculate start min/max values
  48995. // (including min/maxPadding). This is related to using vertical panning
  48996. // (#11315).
  48997. addEvent(Axis, 'afterSetScale', function () {
  48998. var axis = this, panning = axis.chart.options.chart &&
  48999. axis.chart.options.chart.panning;
  49000. if (panning &&
  49001. (panning.type === 'y' ||
  49002. panning.type === 'xy') &&
  49003. !axis.isXAxis &&
  49004. !defined(axis.panningState)) {
  49005. var min = Number.MAX_VALUE, max = Number.MIN_VALUE;
  49006. axis.series.forEach(function (series) {
  49007. min = Math.min(H.arrayMin(series.yData), min) -
  49008. (axis.min && axis.dataMin ? axis.dataMin - axis.min : 0);
  49009. max = Math.max(H.arrayMax(series.yData), max) +
  49010. (axis.max && axis.dataMax ? axis.max - axis.dataMax : 0);
  49011. });
  49012. axis.panningState = {
  49013. startMin: min,
  49014. startMax: max
  49015. };
  49016. }
  49017. });
  49018. });
  49019. _registerModule(_modules, 'masters/modules/stock.src.js', [], function () {
  49020. });
  49021. _registerModule(_modules, 'masters/highstock.src.js', [_modules['masters/highcharts.src.js']], function (Highcharts) {
  49022. Highcharts.product = 'Highstock';
  49023. return Highcharts;
  49024. });
  49025. _modules['masters/highstock.src.js']._modules = _modules;
  49026. return _modules['masters/highstock.src.js'];
  49027. }));