stock.src.js 502 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340
  1. /**
  2. * @license Highstock JS v9.1.1 (2021-06-04)
  3. *
  4. * Highcharts Stock as a plugin for Highcharts
  5. *
  6. * (c) 2010-2021 Torstein Honsi
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/stock', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * (c) 2010-2021 Torstein Honsi
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * */
  41. var isTouchDevice = H.isTouchDevice;
  42. var addEvent = U.addEvent,
  43. correctFloat = U.correctFloat,
  44. defined = U.defined,
  45. isNumber = U.isNumber,
  46. pick = U.pick;
  47. /* eslint-disable valid-jsdoc */
  48. /**
  49. * @private
  50. * @class
  51. */
  52. var NavigatorAxisAdditions = /** @class */ (function () {
  53. /* *
  54. *
  55. * Constructors
  56. *
  57. * */
  58. function NavigatorAxisAdditions(axis) {
  59. this.axis = axis;
  60. }
  61. /* *
  62. *
  63. * Functions
  64. *
  65. * */
  66. /**
  67. * @private
  68. */
  69. NavigatorAxisAdditions.prototype.destroy = function () {
  70. this.axis = void 0;
  71. };
  72. /**
  73. * Add logic to normalize the zoomed range in order to preserve the pressed
  74. * state of range selector buttons
  75. *
  76. * @private
  77. * @function Highcharts.Axis#toFixedRange
  78. * @param {number} [pxMin]
  79. * @param {number} [pxMax]
  80. * @param {number} [fixedMin]
  81. * @param {number} [fixedMax]
  82. * @return {*}
  83. */
  84. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  85. var navigator = this;
  86. var axis = navigator.axis;
  87. var chart = axis.chart;
  88. var fixedRange = chart && chart.fixedRange,
  89. halfPointRange = (axis.pointRange || 0) / 2,
  90. newMin = pick(fixedMin,
  91. axis.translate(pxMin,
  92. true, !axis.horiz)),
  93. newMax = pick(fixedMax,
  94. axis.translate(pxMax,
  95. true, !axis.horiz)),
  96. changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  97. // Add/remove half point range to/from the extremes (#1172)
  98. if (!defined(fixedMin)) {
  99. newMin = correctFloat(newMin + halfPointRange);
  100. }
  101. if (!defined(fixedMax)) {
  102. newMax = correctFloat(newMax - halfPointRange);
  103. }
  104. // If the difference between the fixed range and the actual requested
  105. // range is too great, the user is dragging across an ordinal gap, and
  106. // we need to release the range selector button.
  107. if (changeRatio > 0.7 && changeRatio < 1.3) {
  108. if (fixedMax) {
  109. newMin = newMax - fixedRange;
  110. }
  111. else {
  112. newMax = newMin + fixedRange;
  113. }
  114. }
  115. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  116. newMin = newMax = void 0;
  117. }
  118. return {
  119. min: newMin,
  120. max: newMax
  121. };
  122. };
  123. return NavigatorAxisAdditions;
  124. }());
  125. /**
  126. * @private
  127. * @class
  128. */
  129. var NavigatorAxis = /** @class */ (function () {
  130. function NavigatorAxis() {
  131. }
  132. /* *
  133. *
  134. * Static Functions
  135. *
  136. * */
  137. /**
  138. * @private
  139. */
  140. NavigatorAxis.compose = function (AxisClass) {
  141. AxisClass.keepProps.push('navigatorAxis');
  142. /* eslint-disable no-invalid-this */
  143. addEvent(AxisClass, 'init', function () {
  144. var axis = this;
  145. if (!axis.navigatorAxis) {
  146. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  147. }
  148. });
  149. // For Stock charts, override selection zooming with some special
  150. // features because X axis zooming is already allowed by the Navigator
  151. // and Range selector.
  152. addEvent(AxisClass, 'zoom', function (e) {
  153. var axis = this;
  154. var chart = axis.chart;
  155. var chartOptions = chart.options;
  156. var navigator = chartOptions.navigator;
  157. var navigatorAxis = axis.navigatorAxis;
  158. var pinchType = chartOptions.chart.pinchType;
  159. var rangeSelector = chartOptions.rangeSelector;
  160. var zoomType = chartOptions.chart.zoomType;
  161. var previousZoom;
  162. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  163. (rangeSelector && rangeSelector.enabled))) {
  164. // For y only zooming, ignore the X axis completely
  165. if (zoomType === 'y') {
  166. e.zoomed = false;
  167. // For xy zooming, record the state of the zoom before zoom
  168. // selection, then when the reset button is pressed, revert to
  169. // this state. This should apply only if the chart is
  170. // initialized with a range (#6612), otherwise zoom all the way
  171. // out.
  172. }
  173. else if (((!isTouchDevice && zoomType === 'xy') ||
  174. (isTouchDevice && pinchType === 'xy')) &&
  175. axis.options.range) {
  176. previousZoom = navigatorAxis.previousZoom;
  177. if (defined(e.newMin)) {
  178. navigatorAxis.previousZoom = [axis.min, axis.max];
  179. }
  180. else if (previousZoom) {
  181. e.newMin = previousZoom[0];
  182. e.newMax = previousZoom[1];
  183. navigatorAxis.previousZoom = void 0;
  184. }
  185. }
  186. }
  187. if (typeof e.zoomed !== 'undefined') {
  188. e.preventDefault();
  189. }
  190. });
  191. /* eslint-enable no-invalid-this */
  192. };
  193. /* *
  194. *
  195. * Static Properties
  196. *
  197. * */
  198. /**
  199. * @private
  200. */
  201. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  202. return NavigatorAxis;
  203. }());
  204. return NavigatorAxis;
  205. });
  206. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {
  207. /* *
  208. *
  209. * (c) 2010-2021 Torstein Honsi
  210. *
  211. * License: www.highcharts.com/license
  212. *
  213. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  214. *
  215. * */
  216. var addEvent = U.addEvent,
  217. defined = U.defined,
  218. pick = U.pick;
  219. /* *
  220. *
  221. * Composition
  222. *
  223. * */
  224. /* eslint-disable no-invalid-this, valid-jsdoc */
  225. /**
  226. * Creates scrollbars if enabled.
  227. * @private
  228. */
  229. var ScrollbarAxis = /** @class */ (function () {
  230. function ScrollbarAxis() {
  231. }
  232. /**
  233. * Attaches to axis events to create scrollbars if enabled.
  234. *
  235. * @private
  236. *
  237. * @param AxisClass
  238. * Axis class to extend.
  239. *
  240. * @param ScrollbarClass
  241. * Scrollbar class to use.
  242. */
  243. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  244. var getExtremes = function (axis) {
  245. var axisMin = pick(axis.options && axis.options.min, axis.min);
  246. var axisMax = pick(axis.options && axis.options.max,
  247. axis.max);
  248. return {
  249. axisMin: axisMin,
  250. axisMax: axisMax,
  251. scrollMin: defined(axis.dataMin) ?
  252. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  253. scrollMax: defined(axis.dataMax) ?
  254. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  255. };
  256. };
  257. // Wrap axis initialization and create scrollbar if enabled:
  258. addEvent(AxisClass, 'afterInit', function () {
  259. var axis = this;
  260. if (axis.options &&
  261. axis.options.scrollbar &&
  262. axis.options.scrollbar.enabled) {
  263. // Predefined options:
  264. axis.options.scrollbar.vertical = !axis.horiz;
  265. axis.options.startOnTick = axis.options.endOnTick = false;
  266. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  267. addEvent(axis.scrollbar, 'changed', function (e) {
  268. var _a = getExtremes(axis),
  269. axisMin = _a.axisMin,
  270. axisMax = _a.axisMax,
  271. unitedMin = _a.scrollMin,
  272. unitedMax = _a.scrollMax,
  273. range = unitedMax - unitedMin,
  274. to,
  275. from;
  276. // #12834, scroll when show/hide series, wrong extremes
  277. if (!defined(axisMin) || !defined(axisMax)) {
  278. return;
  279. }
  280. if ((axis.horiz && !axis.reversed) ||
  281. (!axis.horiz && axis.reversed)) {
  282. to = unitedMin + range * this.to;
  283. from = unitedMin + range * this.from;
  284. }
  285. else {
  286. // y-values in browser are reversed, but this also
  287. // applies for reversed horizontal axis:
  288. to = unitedMin + range * (1 - this.from);
  289. from = unitedMin + range * (1 - this.to);
  290. }
  291. if (this.shouldUpdateExtremes(e.DOMType)) {
  292. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e);
  293. }
  294. else {
  295. // When live redraw is disabled, don't change extremes
  296. // Only change the position of the scollbar thumb
  297. this.setRange(this.from, this.to);
  298. }
  299. });
  300. }
  301. });
  302. // Wrap rendering axis, and update scrollbar if one is created:
  303. addEvent(AxisClass, 'afterRender', function () {
  304. var axis = this,
  305. _a = getExtremes(axis),
  306. scrollMin = _a.scrollMin,
  307. scrollMax = _a.scrollMax,
  308. scrollbar = axis.scrollbar,
  309. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  310. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  311. axisMargin = axis.options.margin || 0,
  312. offsetsIndex,
  313. from,
  314. to;
  315. if (scrollbar) {
  316. if (axis.horiz) {
  317. // Reserve space for labels/title
  318. if (!axis.opposite) {
  319. scrollbarsOffsets[1] += offset;
  320. }
  321. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  322. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  323. // Next scrollbar should reserve space for margin (if set)
  324. if (!axis.opposite) {
  325. scrollbarsOffsets[1] += axisMargin;
  326. }
  327. offsetsIndex = 1;
  328. }
  329. else {
  330. // Reserve space for labels/title
  331. if (axis.opposite) {
  332. scrollbarsOffsets[0] += offset;
  333. }
  334. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  335. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  336. // Next scrollbar should reserve space for margin (if set)
  337. if (axis.opposite) {
  338. scrollbarsOffsets[0] += axisMargin;
  339. }
  340. offsetsIndex = 0;
  341. }
  342. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  343. scrollbar.options.margin;
  344. if (isNaN(scrollMin) ||
  345. isNaN(scrollMax) ||
  346. !defined(axis.min) ||
  347. !defined(axis.max) ||
  348. axis.min === axis.max // #10733
  349. ) {
  350. // default action: when extremes are the same or there is
  351. // not extremes on the axis, but scrollbar exists, make it
  352. // full size
  353. scrollbar.setRange(0, 1);
  354. }
  355. else {
  356. from =
  357. (axis.min - scrollMin) / (scrollMax - scrollMin);
  358. to =
  359. (axis.max - scrollMin) / (scrollMax - scrollMin);
  360. if ((axis.horiz && !axis.reversed) ||
  361. (!axis.horiz && axis.reversed)) {
  362. scrollbar.setRange(from, to);
  363. }
  364. else {
  365. // inverse vertical axis
  366. scrollbar.setRange(1 - to, 1 - from);
  367. }
  368. }
  369. }
  370. });
  371. // Make space for a scrollbar:
  372. addEvent(AxisClass, 'afterGetOffset', function () {
  373. var axis = this,
  374. index = axis.horiz ? 2 : 1,
  375. scrollbar = axis.scrollbar;
  376. if (scrollbar) {
  377. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  378. axis.chart.axisOffset[index] +=
  379. scrollbar.size + scrollbar.options.margin;
  380. }
  381. });
  382. return AxisClass;
  383. };
  384. return ScrollbarAxis;
  385. }());
  386. return ScrollbarAxis;
  387. });
  388. _registerModule(_modules, 'Core/ScrollbarDefaults.js', [_modules['Core/Globals.js'], _modules['Core/Color/Palette.js']], function (H, Palette) {
  389. /* *
  390. *
  391. * (c) 2010-2021 Torstein Honsi
  392. *
  393. * License: www.highcharts.com/license
  394. *
  395. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  396. *
  397. * */
  398. var isTouchDevice = H.isTouchDevice;
  399. /* *
  400. *
  401. * Constant
  402. *
  403. * */
  404. /**
  405. *
  406. * The scrollbar is a means of panning over the X axis of a stock chart.
  407. * Scrollbars can also be applied to other types of axes.
  408. *
  409. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  410. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  411. * is especially suitable for simpler cartesian charts on mobile.
  412. *
  413. * In styled mode, all the presentational options for the
  414. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  415. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  416. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  417. *
  418. * @sample stock/yaxis/inverted-bar-scrollbar/
  419. * A scrollbar on a simple bar chart
  420. *
  421. * @product highstock gantt
  422. * @optionparent scrollbar
  423. *
  424. * @private
  425. */
  426. var ScrollbarDefaults = {
  427. /**
  428. * The height of the scrollbar. The height also applies to the width
  429. * of the scroll arrows so that they are always squares. Defaults to
  430. * 20 for touch devices and 14 for mouse devices.
  431. *
  432. * @sample stock/scrollbar/height/
  433. * A 30px scrollbar
  434. *
  435. * @type {number}
  436. * @default 20/14
  437. */
  438. height: isTouchDevice ? 20 : 14,
  439. /**
  440. * The border rounding radius of the bar.
  441. *
  442. * @sample stock/scrollbar/style/
  443. * Scrollbar styling
  444. */
  445. barBorderRadius: 0,
  446. /**
  447. * The corner radius of the scrollbar buttons.
  448. *
  449. * @sample stock/scrollbar/style/
  450. * Scrollbar styling
  451. */
  452. buttonBorderRadius: 0,
  453. /**
  454. * Enable or disable the scrollbar.
  455. *
  456. * @sample stock/scrollbar/enabled/
  457. * Disable the scrollbar,
  458. only use navigator
  459. *
  460. * @type {boolean}
  461. * @default true
  462. * @apioption scrollbar.enabled
  463. */
  464. /**
  465. * Whether to redraw the main chart as the scrollbar or the navigator
  466. * zoomed window is moved. Defaults to `true` for modern browsers and
  467. * `false` for legacy IE browsers as well as mobile devices.
  468. *
  469. * @sample stock/scrollbar/liveredraw
  470. * Setting live redraw to false
  471. *
  472. * @type {boolean}
  473. * @since 1.3
  474. */
  475. liveRedraw: void 0,
  476. /**
  477. * The margin between the scrollbar and its axis when the scrollbar is
  478. * applied directly to an axis.
  479. */
  480. margin: 10,
  481. /**
  482. * The minimum width of the scrollbar.
  483. *
  484. * @since 1.2.5
  485. */
  486. minWidth: 6,
  487. /**
  488. * Whether to show or hide the scrollbar when the scrolled content is
  489. * zoomed out to it full extent.
  490. *
  491. * @type {boolean}
  492. * @default true
  493. * @apioption scrollbar.showFull
  494. */
  495. step: 0.2,
  496. /**
  497. * The z index of the scrollbar group.
  498. */
  499. zIndex: 3,
  500. /**
  501. * The background color of the scrollbar itself.
  502. *
  503. * @sample stock/scrollbar/style/
  504. * Scrollbar styling
  505. *
  506. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  507. */
  508. barBackgroundColor: Palette.neutralColor20,
  509. /**
  510. * The width of the bar's border.
  511. *
  512. * @sample stock/scrollbar/style/
  513. * Scrollbar styling
  514. */
  515. barBorderWidth: 1,
  516. /**
  517. * The color of the scrollbar's border.
  518. *
  519. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  520. */
  521. barBorderColor: Palette.neutralColor20,
  522. /**
  523. * The color of the small arrow inside the scrollbar buttons.
  524. *
  525. * @sample stock/scrollbar/style/
  526. * Scrollbar styling
  527. *
  528. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  529. */
  530. buttonArrowColor: Palette.neutralColor80,
  531. /**
  532. * The color of scrollbar buttons.
  533. *
  534. * @sample stock/scrollbar/style/
  535. * Scrollbar styling
  536. *
  537. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  538. */
  539. buttonBackgroundColor: Palette.neutralColor10,
  540. /**
  541. * The color of the border of the scrollbar buttons.
  542. *
  543. * @sample stock/scrollbar/style/
  544. * Scrollbar styling
  545. *
  546. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  547. */
  548. buttonBorderColor: Palette.neutralColor20,
  549. /**
  550. * The border width of the scrollbar buttons.
  551. *
  552. * @sample stock/scrollbar/style/
  553. * Scrollbar styling
  554. */
  555. buttonBorderWidth: 1,
  556. /**
  557. * The color of the small rifles in the middle of the scrollbar.
  558. *
  559. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  560. */
  561. rifleColor: Palette.neutralColor80,
  562. /**
  563. * The color of the track background.
  564. *
  565. * @sample stock/scrollbar/style/
  566. * Scrollbar styling
  567. *
  568. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  569. */
  570. trackBackgroundColor: Palette.neutralColor5,
  571. /**
  572. * The color of the border of the scrollbar track.
  573. *
  574. * @sample stock/scrollbar/style/
  575. * Scrollbar styling
  576. *
  577. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  578. */
  579. trackBorderColor: Palette.neutralColor5,
  580. /**
  581. * The corner radius of the border of the scrollbar track.
  582. *
  583. * @sample stock/scrollbar/style/
  584. * Scrollbar styling
  585. *
  586. * @type {number}
  587. * @default 0
  588. * @apioption scrollbar.trackBorderRadius
  589. */
  590. /**
  591. * The width of the border of the scrollbar track.
  592. *
  593. * @sample stock/scrollbar/style/
  594. * Scrollbar styling
  595. */
  596. trackBorderWidth: 1
  597. };
  598. /* *
  599. *
  600. * Default Export
  601. *
  602. * */
  603. return ScrollbarDefaults;
  604. });
  605. _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/DefaultOptions.js'], _modules['Core/Globals.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/ScrollbarDefaults.js'], _modules['Core/Utilities.js']], function (D, H, ScrollbarAxis, ScrollbarDefaults, U) {
  606. /* *
  607. *
  608. * (c) 2010-2021 Torstein Honsi
  609. *
  610. * License: www.highcharts.com/license
  611. *
  612. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  613. *
  614. * */
  615. var defaultOptions = D.defaultOptions;
  616. var addEvent = U.addEvent,
  617. correctFloat = U.correctFloat,
  618. defined = U.defined,
  619. destroyObjectProperties = U.destroyObjectProperties,
  620. fireEvent = U.fireEvent,
  621. merge = U.merge,
  622. pick = U.pick,
  623. removeEvent = U.removeEvent;
  624. /* *
  625. *
  626. * Constants
  627. *
  628. * */
  629. /* eslint-disable no-invalid-this, valid-jsdoc */
  630. /**
  631. * A reusable scrollbar, internally used in Highcharts Stock's
  632. * navigator and optionally on individual axes.
  633. *
  634. * @private
  635. * @class
  636. * @name Highcharts.Scrollbar
  637. * @param {Highcharts.SVGRenderer} renderer
  638. * @param {Highcharts.ScrollbarOptions} options
  639. * @param {Highcharts.Chart} chart
  640. */
  641. var Scrollbar = /** @class */ (function () {
  642. /* *
  643. *
  644. * Constructors
  645. *
  646. * */
  647. function Scrollbar(renderer, options, chart) {
  648. /* *
  649. *
  650. * Properties
  651. *
  652. * */
  653. this._events = [];
  654. this.chart = void 0;
  655. this.chartX = 0;
  656. this.chartY = 0;
  657. this.from = 0;
  658. this.group = void 0;
  659. this.options = void 0;
  660. this.renderer = void 0;
  661. this.scrollbar = void 0;
  662. this.scrollbarButtons = [];
  663. this.scrollbarGroup = void 0;
  664. this.scrollbarLeft = 0;
  665. this.scrollbarRifles = void 0;
  666. this.scrollbarStrokeWidth = 1;
  667. this.scrollbarTop = 0;
  668. this.size = 0;
  669. this.to = 0;
  670. this.track = void 0;
  671. this.trackBorderWidth = 1;
  672. this.userOptions = void 0;
  673. this.x = 0;
  674. this.y = 0;
  675. this.init(renderer, options, chart);
  676. }
  677. /* *
  678. *
  679. * Static Functions
  680. *
  681. * */
  682. Scrollbar.compose = function (AxisClass) {
  683. ScrollbarAxis.compose(AxisClass, Scrollbar);
  684. };
  685. /**
  686. * When we have vertical scrollbar, rifles and arrow in buttons should be
  687. * rotated. The same method is used in Navigator's handles, to rotate them.
  688. *
  689. * @function Highcharts.swapXY
  690. *
  691. * @param {Highcharts.SVGPathArray} path
  692. * Path to be rotated.
  693. *
  694. * @param {boolean} [vertical]
  695. * If vertical scrollbar, swap x-y values.
  696. *
  697. * @return {Highcharts.SVGPathArray}
  698. * Rotated path.
  699. *
  700. * @requires modules/stock
  701. */
  702. Scrollbar.swapXY = function (path, vertical) {
  703. if (vertical) {
  704. path.forEach(function (seg) {
  705. var len = seg.length;
  706. var temp;
  707. for (var i = 0; i < len; i += 2) {
  708. temp = seg[i + 1];
  709. if (typeof temp === 'number') {
  710. seg[i + 1] = seg[i + 2];
  711. seg[i + 2] = temp;
  712. }
  713. }
  714. });
  715. }
  716. return path;
  717. };
  718. /* *
  719. *
  720. * Functions
  721. *
  722. * */
  723. /**
  724. * Set up the mouse and touch events for the Scrollbar
  725. *
  726. * @private
  727. * @function Highcharts.Scrollbar#addEvents
  728. */
  729. Scrollbar.prototype.addEvents = function () {
  730. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  731. buttons = this.scrollbarButtons,
  732. bar = this.scrollbarGroup.element,
  733. track = this.track.element,
  734. mouseDownHandler = this.mouseDownHandler.bind(this),
  735. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  736. mouseUpHandler = this.mouseUpHandler.bind(this);
  737. // Mouse events
  738. var _events = [
  739. [buttons[buttonsOrder[0]].element, 'click',
  740. this.buttonToMinClick.bind(this)],
  741. [buttons[buttonsOrder[1]].element, 'click',
  742. this.buttonToMaxClick.bind(this)],
  743. [track, 'click',
  744. this.trackClick.bind(this)],
  745. [bar, 'mousedown',
  746. mouseDownHandler],
  747. [bar.ownerDocument, 'mousemove',
  748. mouseMoveHandler],
  749. [bar.ownerDocument, 'mouseup',
  750. mouseUpHandler]
  751. ];
  752. // Touch events
  753. if (H.hasTouch) {
  754. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  755. }
  756. // Add them all
  757. _events.forEach(function (args) {
  758. addEvent.apply(null, args);
  759. });
  760. this._events = _events;
  761. };
  762. Scrollbar.prototype.buttonToMaxClick = function (e) {
  763. var scroller = this;
  764. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  765. scroller.updatePosition(scroller.from + range, scroller.to + range);
  766. fireEvent(scroller, 'changed', {
  767. from: scroller.from,
  768. to: scroller.to,
  769. trigger: 'scrollbar',
  770. DOMEvent: e
  771. });
  772. };
  773. Scrollbar.prototype.buttonToMinClick = function (e) {
  774. var scroller = this;
  775. var range = correctFloat(scroller.to - scroller.from) *
  776. pick(scroller.options.step, 0.2);
  777. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  778. fireEvent(scroller, 'changed', {
  779. from: scroller.from,
  780. to: scroller.to,
  781. trigger: 'scrollbar',
  782. DOMEvent: e
  783. });
  784. };
  785. /**
  786. * Get normalized (0-1) cursor position over the scrollbar
  787. *
  788. * @private
  789. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  790. *
  791. * @param {*} normalizedEvent
  792. * normalized event, with chartX and chartY values
  793. *
  794. * @return {Highcharts.Dictionary<number>}
  795. * Local position {chartX, chartY}
  796. */
  797. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  798. var scroller = this,
  799. options = scroller.options,
  800. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  801. options.minWidth :
  802. 0; // minWidth distorts translation
  803. return {
  804. chartX: (normalizedEvent.chartX - scroller.x -
  805. scroller.xOffset) /
  806. (scroller.barWidth - minWidthDifference),
  807. chartY: (normalizedEvent.chartY - scroller.y -
  808. scroller.yOffset) /
  809. (scroller.barWidth - minWidthDifference)
  810. };
  811. };
  812. /**
  813. * Destroys allocated elements.
  814. *
  815. * @private
  816. * @function Highcharts.Scrollbar#destroy
  817. * @return {void}
  818. */
  819. Scrollbar.prototype.destroy = function () {
  820. var scroller = this,
  821. navigator = scroller.chart.scroller;
  822. // Disconnect events added in addEvents
  823. scroller.removeEvents();
  824. // Destroy properties
  825. [
  826. 'track',
  827. 'scrollbarRifles',
  828. 'scrollbar',
  829. 'scrollbarGroup',
  830. 'group'
  831. ].forEach(function (prop) {
  832. if (scroller[prop] && scroller[prop].destroy) {
  833. scroller[prop] = scroller[prop].destroy();
  834. }
  835. });
  836. // #6421, chart may have more scrollbars
  837. if (navigator && scroller === navigator.scrollbar) {
  838. navigator.scrollbar = null;
  839. // Destroy elements in collection
  840. destroyObjectProperties(navigator.scrollbarButtons);
  841. }
  842. };
  843. /**
  844. * Draw the scrollbar buttons with arrows
  845. *
  846. * @private
  847. * @function Highcharts.Scrollbar#drawScrollbarButton
  848. * @param {number} index
  849. * 0 is left, 1 is right
  850. * @return {void}
  851. */
  852. Scrollbar.prototype.drawScrollbarButton = function (index) {
  853. var scroller = this,
  854. renderer = scroller.renderer,
  855. scrollbarButtons = scroller.scrollbarButtons,
  856. options = scroller.options,
  857. size = scroller.size,
  858. group = renderer.g().add(scroller.group);
  859. var tempElem;
  860. scrollbarButtons.push(group);
  861. // Create a rectangle for the scrollbar button
  862. tempElem = renderer.rect()
  863. .addClass('highcharts-scrollbar-button')
  864. .add(group);
  865. // Presentational attributes
  866. if (!scroller.chart.styledMode) {
  867. tempElem.attr({
  868. stroke: options.buttonBorderColor,
  869. 'stroke-width': options.buttonBorderWidth,
  870. fill: options.buttonBackgroundColor
  871. });
  872. }
  873. // Place the rectangle based on the rendered stroke width
  874. tempElem.attr(tempElem.crisp({
  875. x: -0.5,
  876. y: -0.5,
  877. width: size + 1,
  878. height: size + 1,
  879. r: options.buttonBorderRadius
  880. }, tempElem.strokeWidth()));
  881. // Button arrow
  882. tempElem = renderer
  883. .path(Scrollbar.swapXY([[
  884. 'M',
  885. size / 2 + (index ? -1 : 1),
  886. size / 2 - 3
  887. ], [
  888. 'L',
  889. size / 2 + (index ? -1 : 1),
  890. size / 2 + 3
  891. ], [
  892. 'L',
  893. size / 2 + (index ? 2 : -2),
  894. size / 2
  895. ]], options.vertical))
  896. .addClass('highcharts-scrollbar-arrow')
  897. .add(scrollbarButtons[index]);
  898. if (!scroller.chart.styledMode) {
  899. tempElem.attr({
  900. fill: options.buttonArrowColor
  901. });
  902. }
  903. };
  904. /**
  905. * @private
  906. * @function Highcharts.Scrollbar#init
  907. * @param {Highcharts.SVGRenderer} renderer
  908. * @param {Highcharts.ScrollbarOptions} options
  909. * @param {Highcharts.Chart} chart
  910. */
  911. Scrollbar.prototype.init = function (renderer, options, chart) {
  912. var scroller = this;
  913. scroller.scrollbarButtons = [];
  914. scroller.renderer = renderer;
  915. scroller.userOptions = options;
  916. scroller.options = merge(ScrollbarDefaults, defaultOptions.scrollbar, options);
  917. scroller.chart = chart;
  918. // backward compatibility
  919. scroller.size = pick(scroller.options.size, scroller.options.height);
  920. // Init
  921. if (options.enabled) {
  922. scroller.render();
  923. scroller.addEvents();
  924. }
  925. };
  926. Scrollbar.prototype.mouseDownHandler = function (e) {
  927. var scroller = this,
  928. normalizedEvent = scroller.chart.pointer.normalize(e),
  929. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  930. scroller.chartX = mousePosition.chartX;
  931. scroller.chartY = mousePosition.chartY;
  932. scroller.initPositions = [scroller.from, scroller.to];
  933. scroller.grabbedCenter = true;
  934. };
  935. /**
  936. * Event handler for the mouse move event.
  937. * @private
  938. */
  939. Scrollbar.prototype.mouseMoveHandler = function (e) {
  940. var scroller = this,
  941. normalizedEvent = scroller.chart.pointer.normalize(e),
  942. options = scroller.options,
  943. direction = options.vertical ? 'chartY' : 'chartX',
  944. initPositions = scroller.initPositions || [];
  945. var scrollPosition,
  946. chartPosition,
  947. change;
  948. // In iOS, a mousemove event with e.pageX === 0 is fired when
  949. // holding the finger down in the center of the scrollbar. This
  950. // should be ignored.
  951. if (scroller.grabbedCenter &&
  952. // #4696, scrollbar failed on Android
  953. (!e.touches || e.touches[0][direction] !== 0)) {
  954. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  955. scrollPosition = scroller[direction];
  956. change = chartPosition - scrollPosition;
  957. scroller.hasDragged = true;
  958. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  959. if (scroller.hasDragged) {
  960. fireEvent(scroller, 'changed', {
  961. from: scroller.from,
  962. to: scroller.to,
  963. trigger: 'scrollbar',
  964. DOMType: e.type,
  965. DOMEvent: e
  966. });
  967. }
  968. }
  969. };
  970. /**
  971. * Event handler for the mouse up event.
  972. * @private
  973. */
  974. Scrollbar.prototype.mouseUpHandler = function (e) {
  975. var scroller = this;
  976. if (scroller.hasDragged) {
  977. fireEvent(scroller, 'changed', {
  978. from: scroller.from,
  979. to: scroller.to,
  980. trigger: 'scrollbar',
  981. DOMType: e.type,
  982. DOMEvent: e
  983. });
  984. }
  985. scroller.grabbedCenter =
  986. scroller.hasDragged =
  987. scroller.chartX =
  988. scroller.chartY = null;
  989. };
  990. /**
  991. * Position the scrollbar, method called from a parent with defined
  992. * dimensions.
  993. *
  994. * @private
  995. * @function Highcharts.Scrollbar#position
  996. * @param {number} x
  997. * x-position on the chart
  998. * @param {number} y
  999. * y-position on the chart
  1000. * @param {number} width
  1001. * width of the scrollbar
  1002. * @param {number} height
  1003. * height of the scorllbar
  1004. * @return {void}
  1005. */
  1006. Scrollbar.prototype.position = function (x, y, width, height) {
  1007. var scroller = this,
  1008. options = scroller.options,
  1009. vertical = options.vertical,
  1010. method = scroller.rendered ? 'animate' : 'attr';
  1011. var xOffset = height,
  1012. yOffset = 0;
  1013. scroller.x = x;
  1014. scroller.y = y + this.trackBorderWidth;
  1015. scroller.width = width; // width with buttons
  1016. scroller.height = height;
  1017. scroller.xOffset = xOffset;
  1018. scroller.yOffset = yOffset;
  1019. // If Scrollbar is a vertical type, swap options:
  1020. if (vertical) {
  1021. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  1022. scroller.xOffset = xOffset = 0;
  1023. scroller.barWidth = height - width * 2; // width without buttons
  1024. scroller.x = x = x + scroller.options.margin;
  1025. }
  1026. else {
  1027. scroller.height = scroller.xOffset = height = xOffset =
  1028. scroller.size;
  1029. scroller.barWidth = width - height * 2; // width without buttons
  1030. scroller.y = scroller.y + scroller.options.margin;
  1031. }
  1032. // Set general position for a group:
  1033. scroller.group[method]({
  1034. translateX: x,
  1035. translateY: scroller.y
  1036. });
  1037. // Resize background/track:
  1038. scroller.track[method]({
  1039. width: width,
  1040. height: height
  1041. });
  1042. // Move right/bottom button ot it's place:
  1043. scroller.scrollbarButtons[1][method]({
  1044. translateX: vertical ? 0 : width - xOffset,
  1045. translateY: vertical ? height - yOffset : 0
  1046. });
  1047. };
  1048. /**
  1049. * Removes the event handlers attached previously with addEvents.
  1050. *
  1051. * @private
  1052. * @function Highcharts.Scrollbar#removeEvents
  1053. * @return {void}
  1054. */
  1055. Scrollbar.prototype.removeEvents = function () {
  1056. this._events.forEach(function (args) {
  1057. removeEvent.apply(null, args);
  1058. });
  1059. this._events.length = 0;
  1060. };
  1061. /**
  1062. * Render scrollbar with all required items.
  1063. *
  1064. * @private
  1065. * @function Highcharts.Scrollbar#render
  1066. */
  1067. Scrollbar.prototype.render = function () {
  1068. var scroller = this,
  1069. renderer = scroller.renderer,
  1070. options = scroller.options,
  1071. size = scroller.size,
  1072. styledMode = scroller.chart.styledMode,
  1073. group = renderer.g('scrollbar').attr({
  1074. zIndex: options.zIndex,
  1075. translateY: -99999
  1076. }).add();
  1077. // Draw the scrollbar group
  1078. scroller.group = group;
  1079. // Draw the scrollbar track:
  1080. scroller.track = renderer.rect()
  1081. .addClass('highcharts-scrollbar-track')
  1082. .attr({
  1083. x: 0,
  1084. r: options.trackBorderRadius || 0,
  1085. height: size,
  1086. width: size
  1087. }).add(group);
  1088. if (!styledMode) {
  1089. scroller.track.attr({
  1090. fill: options.trackBackgroundColor,
  1091. stroke: options.trackBorderColor,
  1092. 'stroke-width': options.trackBorderWidth
  1093. });
  1094. }
  1095. scroller.trackBorderWidth = scroller.track.strokeWidth();
  1096. scroller.track.attr({
  1097. y: -this.trackBorderWidth % 2 / 2
  1098. });
  1099. // Draw the scrollbar itself
  1100. scroller.scrollbarGroup = renderer.g().add(group);
  1101. scroller.scrollbar = renderer.rect()
  1102. .addClass('highcharts-scrollbar-thumb')
  1103. .attr({
  1104. height: size,
  1105. width: size,
  1106. r: options.barBorderRadius || 0
  1107. }).add(scroller.scrollbarGroup);
  1108. scroller.scrollbarRifles = renderer
  1109. .path(Scrollbar.swapXY([
  1110. ['M', -3, size / 4],
  1111. ['L', -3, 2 * size / 3],
  1112. ['M', 0, size / 4],
  1113. ['L', 0, 2 * size / 3],
  1114. ['M', 3, size / 4],
  1115. ['L', 3, 2 * size / 3]
  1116. ], options.vertical))
  1117. .addClass('highcharts-scrollbar-rifles')
  1118. .add(scroller.scrollbarGroup);
  1119. if (!styledMode) {
  1120. scroller.scrollbar.attr({
  1121. fill: options.barBackgroundColor,
  1122. stroke: options.barBorderColor,
  1123. 'stroke-width': options.barBorderWidth
  1124. });
  1125. scroller.scrollbarRifles.attr({
  1126. stroke: options.rifleColor,
  1127. 'stroke-width': 1
  1128. });
  1129. }
  1130. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  1131. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  1132. // Draw the buttons:
  1133. scroller.drawScrollbarButton(0);
  1134. scroller.drawScrollbarButton(1);
  1135. };
  1136. /**
  1137. * Set scrollbar size, with a given scale.
  1138. *
  1139. * @private
  1140. * @function Highcharts.Scrollbar#setRange
  1141. * @param {number} from
  1142. * scale (0-1) where bar should start
  1143. * @param {number} to
  1144. * scale (0-1) where bar should end
  1145. * @return {void}
  1146. */
  1147. Scrollbar.prototype.setRange = function (from, to) {
  1148. var scroller = this,
  1149. options = scroller.options,
  1150. vertical = options.vertical,
  1151. minWidth = options.minWidth,
  1152. fullWidth = scroller.barWidth,
  1153. method = (this.rendered &&
  1154. !this.hasDragged &&
  1155. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  1156. if (!defined(fullWidth)) {
  1157. return;
  1158. }
  1159. var toPX = fullWidth * Math.min(to, 1);
  1160. var fromPX,
  1161. newSize;
  1162. from = Math.max(from, 0);
  1163. fromPX = Math.ceil(fullWidth * from);
  1164. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  1165. // We need to recalculate position, if minWidth is used
  1166. if (newSize < minWidth) {
  1167. fromPX = (fullWidth - minWidth + newSize) * from;
  1168. newSize = minWidth;
  1169. }
  1170. var newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  1171. var newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  1172. // Store current position:
  1173. scroller.from = from;
  1174. scroller.to = to;
  1175. if (!vertical) {
  1176. scroller.scrollbarGroup[method]({
  1177. translateX: newPos
  1178. });
  1179. scroller.scrollbar[method]({
  1180. width: newSize
  1181. });
  1182. scroller.scrollbarRifles[method]({
  1183. translateX: newRiflesPos
  1184. });
  1185. scroller.scrollbarLeft = newPos;
  1186. scroller.scrollbarTop = 0;
  1187. }
  1188. else {
  1189. scroller.scrollbarGroup[method]({
  1190. translateY: newPos
  1191. });
  1192. scroller.scrollbar[method]({
  1193. height: newSize
  1194. });
  1195. scroller.scrollbarRifles[method]({
  1196. translateY: newRiflesPos
  1197. });
  1198. scroller.scrollbarTop = newPos;
  1199. scroller.scrollbarLeft = 0;
  1200. }
  1201. if (newSize <= 12) {
  1202. scroller.scrollbarRifles.hide();
  1203. }
  1204. else {
  1205. scroller.scrollbarRifles.show(true);
  1206. }
  1207. // Show or hide the scrollbar based on the showFull setting
  1208. if (options.showFull === false) {
  1209. if (from <= 0 && to >= 1) {
  1210. scroller.group.hide();
  1211. }
  1212. else {
  1213. scroller.group.show();
  1214. }
  1215. }
  1216. scroller.rendered = true;
  1217. };
  1218. /**
  1219. * Checks if the extremes should be updated in response to a scrollbar
  1220. * change event.
  1221. *
  1222. * @private
  1223. * @function Highcharts.Scrollbar#shouldUpdateExtremes
  1224. * @param {string} [eventType]
  1225. * @return {boolean}
  1226. */
  1227. Scrollbar.prototype.shouldUpdateExtremes = function (eventType) {
  1228. return (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  1229. // Mouseup always should change extremes
  1230. eventType === 'mouseup' ||
  1231. eventType === 'touchend' ||
  1232. // Internal events
  1233. !defined(eventType));
  1234. };
  1235. Scrollbar.prototype.trackClick = function (e) {
  1236. var scroller = this;
  1237. var normalizedEvent = scroller.chart.pointer.normalize(e),
  1238. range = scroller.to - scroller.from,
  1239. top = scroller.y + scroller.scrollbarTop,
  1240. left = scroller.x + scroller.scrollbarLeft;
  1241. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  1242. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  1243. // On the top or on the left side of the track:
  1244. scroller.updatePosition(scroller.from + range, scroller.to + range);
  1245. }
  1246. else {
  1247. // On the bottom or the right side of the track:
  1248. scroller.updatePosition(scroller.from - range, scroller.to - range);
  1249. }
  1250. fireEvent(scroller, 'changed', {
  1251. from: scroller.from,
  1252. to: scroller.to,
  1253. trigger: 'scrollbar',
  1254. DOMEvent: e
  1255. });
  1256. };
  1257. /**
  1258. * Update the scrollbar with new options
  1259. *
  1260. * @private
  1261. * @function Highcharts.Scrollbar#update
  1262. * @param {Highcharts.ScrollbarOptions} options
  1263. */
  1264. Scrollbar.prototype.update = function (options) {
  1265. this.destroy();
  1266. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  1267. };
  1268. /**
  1269. * Update position option in the Scrollbar, with normalized 0-1 scale
  1270. *
  1271. * @private
  1272. * @function Highcharts.Scrollbar#updatePosition
  1273. * @param {number} from
  1274. * @param {number} to
  1275. * @return {void}
  1276. */
  1277. Scrollbar.prototype.updatePosition = function (from, to) {
  1278. if (to > 1) {
  1279. from = correctFloat(1 - correctFloat(to - from));
  1280. to = 1;
  1281. }
  1282. if (from < 0) {
  1283. to = correctFloat(to - from);
  1284. from = 0;
  1285. }
  1286. this.from = from;
  1287. this.to = to;
  1288. };
  1289. /* *
  1290. *
  1291. * Static Properties
  1292. *
  1293. * */
  1294. Scrollbar.defaultOptions = ScrollbarDefaults;
  1295. return Scrollbar;
  1296. }());
  1297. /* *
  1298. *
  1299. * Registry
  1300. *
  1301. * */
  1302. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  1303. /* *
  1304. *
  1305. * Default Export
  1306. *
  1307. * */
  1308. return Scrollbar;
  1309. });
  1310. _registerModule(_modules, 'Core/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxis.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Color/Palette.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Scrollbar.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, D, Palette, RendererRegistry, Scrollbar, Series, SeriesRegistry, U) {
  1311. /* *
  1312. *
  1313. * (c) 2010-2021 Torstein Honsi
  1314. *
  1315. * License: www.highcharts.com/license
  1316. *
  1317. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1318. *
  1319. * */
  1320. var color = Color.parse;
  1321. var hasTouch = H.hasTouch,
  1322. isTouchDevice = H.isTouchDevice;
  1323. var defaultOptions = D.defaultOptions;
  1324. var seriesTypes = SeriesRegistry.seriesTypes;
  1325. var addEvent = U.addEvent,
  1326. clamp = U.clamp,
  1327. correctFloat = U.correctFloat,
  1328. defined = U.defined,
  1329. destroyObjectProperties = U.destroyObjectProperties,
  1330. erase = U.erase,
  1331. extend = U.extend,
  1332. find = U.find,
  1333. isArray = U.isArray,
  1334. isNumber = U.isNumber,
  1335. merge = U.merge,
  1336. pick = U.pick,
  1337. removeEvent = U.removeEvent,
  1338. splat = U.splat;
  1339. var defaultSeriesType,
  1340. // Finding the min or max of a set of variables where we don't know if they
  1341. // are defined, is a pattern that is repeated several places in Highcharts.
  1342. // Consider making this a global utility method.
  1343. numExt = function (extreme) {
  1344. var args = [];
  1345. for (var _i = 1; _i < arguments.length; _i++) {
  1346. args[_i - 1] = arguments[_i];
  1347. }
  1348. var numbers = [].filter.call(args,
  1349. isNumber);
  1350. if (numbers.length) {
  1351. return Math[extreme].apply(0, numbers);
  1352. }
  1353. };
  1354. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  1355. 'line' :
  1356. 'areaspline';
  1357. extend(defaultOptions, {
  1358. /**
  1359. * Maximum range which can be set using the navigator's handles.
  1360. * Opposite of [xAxis.minRange](#xAxis.minRange).
  1361. *
  1362. * @sample {highstock} stock/navigator/maxrange/
  1363. * Defined max and min range
  1364. *
  1365. * @type {number}
  1366. * @since 6.0.0
  1367. * @product highstock gantt
  1368. * @apioption xAxis.maxRange
  1369. */
  1370. /**
  1371. * The navigator is a small series below the main series, displaying
  1372. * a view of the entire data set. It provides tools to zoom in and
  1373. * out on parts of the data as well as panning across the dataset.
  1374. *
  1375. * @product highstock gantt
  1376. * @optionparent navigator
  1377. */
  1378. navigator: {
  1379. /**
  1380. * Whether the navigator and scrollbar should adapt to updated data
  1381. * in the base X axis. When loading data async, as in the demo below,
  1382. * this should be `false`. Otherwise new data will trigger navigator
  1383. * redraw, which will cause unwanted looping. In the demo below, the
  1384. * data in the navigator is set only once. On navigating, only the main
  1385. * chart content is updated.
  1386. *
  1387. * @sample {highstock} stock/demo/lazy-loading/
  1388. * Set to false with async data loading
  1389. *
  1390. * @type {boolean}
  1391. * @default true
  1392. * @apioption navigator.adaptToUpdatedData
  1393. */
  1394. /**
  1395. * An integer identifying the index to use for the base series, or a
  1396. * string representing the id of the series.
  1397. *
  1398. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  1399. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  1400. *
  1401. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  1402. *
  1403. * @deprecated
  1404. * @type {number|string}
  1405. * @default 0
  1406. * @apioption navigator.baseSeries
  1407. */
  1408. /**
  1409. * Enable or disable the navigator.
  1410. *
  1411. * @sample {highstock} stock/navigator/enabled/
  1412. * Disable the navigator
  1413. *
  1414. * @type {boolean}
  1415. * @default true
  1416. * @apioption navigator.enabled
  1417. */
  1418. /**
  1419. * When the chart is inverted, whether to draw the navigator on the
  1420. * opposite side.
  1421. *
  1422. * @type {boolean}
  1423. * @default false
  1424. * @since 5.0.8
  1425. * @apioption navigator.opposite
  1426. */
  1427. /**
  1428. * The height of the navigator.
  1429. *
  1430. * @sample {highstock} stock/navigator/height/
  1431. * A higher navigator
  1432. */
  1433. height: 40,
  1434. /**
  1435. * The distance from the nearest element, the X axis or X axis labels.
  1436. *
  1437. * @sample {highstock} stock/navigator/margin/
  1438. * A margin of 2 draws the navigator closer to the X axis labels
  1439. */
  1440. margin: 25,
  1441. /**
  1442. * Whether the mask should be inside the range marking the zoomed
  1443. * range, or outside. In Highcharts Stock 1.x it was always `false`.
  1444. *
  1445. * @sample {highstock} stock/navigator/maskinside-false/
  1446. * False, mask outside
  1447. *
  1448. * @since 2.0
  1449. */
  1450. maskInside: true,
  1451. /**
  1452. * Options for the handles for dragging the zoomed area.
  1453. *
  1454. * @sample {highstock} stock/navigator/handles/
  1455. * Colored handles
  1456. */
  1457. handles: {
  1458. /**
  1459. * Width for handles.
  1460. *
  1461. * @sample {highstock} stock/navigator/styled-handles/
  1462. * Styled handles
  1463. *
  1464. * @since 6.0.0
  1465. */
  1466. width: 7,
  1467. /**
  1468. * Height for handles.
  1469. *
  1470. * @sample {highstock} stock/navigator/styled-handles/
  1471. * Styled handles
  1472. *
  1473. * @since 6.0.0
  1474. */
  1475. height: 15,
  1476. /**
  1477. * Array to define shapes of handles. 0-index for left, 1-index for
  1478. * right.
  1479. *
  1480. * Additionally, the URL to a graphic can be given on this form:
  1481. * `url(graphic.png)`. Note that for the image to be applied to
  1482. * exported charts, its URL needs to be accessible by the export
  1483. * server.
  1484. *
  1485. * Custom callbacks for symbol path generation can also be added to
  1486. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  1487. * used by its method name, as shown in the demo.
  1488. *
  1489. * @sample {highstock} stock/navigator/styled-handles/
  1490. * Styled handles
  1491. *
  1492. * @type {Array<string>}
  1493. * @default ["navigator-handle", "navigator-handle"]
  1494. * @since 6.0.0
  1495. */
  1496. symbols: ['navigator-handle', 'navigator-handle'],
  1497. /**
  1498. * Allows to enable/disable handles.
  1499. *
  1500. * @since 6.0.0
  1501. */
  1502. enabled: true,
  1503. /**
  1504. * The width for the handle border and the stripes inside.
  1505. *
  1506. * @sample {highstock} stock/navigator/styled-handles/
  1507. * Styled handles
  1508. *
  1509. * @since 6.0.0
  1510. * @apioption navigator.handles.lineWidth
  1511. */
  1512. lineWidth: 1,
  1513. /**
  1514. * The fill for the handle.
  1515. *
  1516. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1517. */
  1518. backgroundColor: Palette.neutralColor5,
  1519. /**
  1520. * The stroke for the handle border and the stripes inside.
  1521. *
  1522. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1523. */
  1524. borderColor: Palette.neutralColor40
  1525. },
  1526. /**
  1527. * The color of the mask covering the areas of the navigator series
  1528. * that are currently not visible in the main series. The default
  1529. * color is bluish with an opacity of 0.3 to see the series below.
  1530. *
  1531. * @see In styled mode, the mask is styled with the
  1532. * `.highcharts-navigator-mask` and
  1533. * `.highcharts-navigator-mask-inside` classes.
  1534. *
  1535. * @sample {highstock} stock/navigator/maskfill/
  1536. * Blue, semi transparent mask
  1537. *
  1538. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1539. * @default rgba(102,133,194,0.3)
  1540. */
  1541. maskFill: color(Palette.highlightColor60).setOpacity(0.3).get(),
  1542. /**
  1543. * The color of the line marking the currently zoomed area in the
  1544. * navigator.
  1545. *
  1546. * @sample {highstock} stock/navigator/outline/
  1547. * 2px blue outline
  1548. *
  1549. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1550. * @default #cccccc
  1551. */
  1552. outlineColor: Palette.neutralColor20,
  1553. /**
  1554. * The width of the line marking the currently zoomed area in the
  1555. * navigator.
  1556. *
  1557. * @see In styled mode, the outline stroke width is set with the
  1558. * `.highcharts-navigator-outline` class.
  1559. *
  1560. * @sample {highstock} stock/navigator/outline/
  1561. * 2px blue outline
  1562. *
  1563. * @type {number}
  1564. */
  1565. outlineWidth: 1,
  1566. /**
  1567. * Options for the navigator series. Available options are the same
  1568. * as any series, documented at [plotOptions](#plotOptions.series)
  1569. * and [series](#series).
  1570. *
  1571. * Unless data is explicitly defined on navigator.series, the data
  1572. * is borrowed from the first series in the chart.
  1573. *
  1574. * Default series options for the navigator series are:
  1575. * ```js
  1576. * series: {
  1577. * type: 'areaspline',
  1578. * fillOpacity: 0.05,
  1579. * dataGrouping: {
  1580. * smoothed: true
  1581. * },
  1582. * lineWidth: 1,
  1583. * marker: {
  1584. * enabled: false
  1585. * }
  1586. * }
  1587. * ```
  1588. *
  1589. * @see In styled mode, the navigator series is styled with the
  1590. * `.highcharts-navigator-series` class.
  1591. *
  1592. * @sample {highstock} stock/navigator/series-data/
  1593. * Using a separate data set for the navigator
  1594. * @sample {highstock} stock/navigator/series/
  1595. * A green navigator series
  1596. *
  1597. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  1598. */
  1599. series: {
  1600. /**
  1601. * The type of the navigator series.
  1602. *
  1603. * Heads up:
  1604. * In column-type navigator, zooming is limited to at least one
  1605. * point with its `pointRange`.
  1606. *
  1607. * @sample {highstock} stock/navigator/column/
  1608. * Column type navigator
  1609. *
  1610. * @type {string}
  1611. * @default {highstock} `areaspline` if defined, otherwise `line`
  1612. * @default {gantt} gantt
  1613. */
  1614. type: defaultSeriesType,
  1615. /**
  1616. * The fill opacity of the navigator series.
  1617. */
  1618. fillOpacity: 0.05,
  1619. /**
  1620. * The pixel line width of the navigator series.
  1621. */
  1622. lineWidth: 1,
  1623. /**
  1624. * @ignore-option
  1625. */
  1626. compare: null,
  1627. /**
  1628. * Unless data is explicitly defined, the data is borrowed from the
  1629. * first series in the chart.
  1630. *
  1631. * @type {Array<number|Array<number|string|null>|object|null>}
  1632. * @product highstock
  1633. * @apioption navigator.series.data
  1634. */
  1635. /**
  1636. * Data grouping options for the navigator series.
  1637. *
  1638. * @extends plotOptions.series.dataGrouping
  1639. */
  1640. dataGrouping: {
  1641. approximation: 'average',
  1642. enabled: true,
  1643. groupPixelWidth: 2,
  1644. // Replace smoothed property by anchors, #12455.
  1645. firstAnchor: 'firstPoint',
  1646. anchor: 'middle',
  1647. lastAnchor: 'lastPoint',
  1648. // Day and week differs from plotOptions.series.dataGrouping
  1649. units: [
  1650. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  1651. ['second', [1, 2, 5, 10, 15, 30]],
  1652. ['minute', [1, 2, 5, 10, 15, 30]],
  1653. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  1654. ['day', [1, 2, 3, 4]],
  1655. ['week', [1, 2, 3]],
  1656. ['month', [1, 3, 6]],
  1657. ['year', null]
  1658. ]
  1659. },
  1660. /**
  1661. * Data label options for the navigator series. Data labels are
  1662. * disabled by default on the navigator series.
  1663. *
  1664. * @extends plotOptions.series.dataLabels
  1665. */
  1666. dataLabels: {
  1667. enabled: false,
  1668. zIndex: 2 // #1839
  1669. },
  1670. id: 'highcharts-navigator-series',
  1671. className: 'highcharts-navigator-series',
  1672. /**
  1673. * Sets the fill color of the navigator series.
  1674. *
  1675. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1676. * @apioption navigator.series.color
  1677. */
  1678. /**
  1679. * Line color for the navigator series. Allows setting the color
  1680. * while disallowing the default candlestick setting.
  1681. *
  1682. * @type {Highcharts.ColorString|null}
  1683. */
  1684. lineColor: null,
  1685. marker: {
  1686. enabled: false
  1687. },
  1688. /**
  1689. * Since Highcharts Stock v8, default value is the same as default
  1690. * `pointRange` defined for a specific type (e.g. `null` for
  1691. * column type).
  1692. *
  1693. * In Highcharts Stock version < 8, defaults to 0.
  1694. *
  1695. * @extends plotOptions.series.pointRange
  1696. * @type {number|null}
  1697. * @apioption navigator.series.pointRange
  1698. */
  1699. /**
  1700. * The threshold option. Setting it to 0 will make the default
  1701. * navigator area series draw its area from the 0 value and up.
  1702. *
  1703. * @type {number|null}
  1704. */
  1705. threshold: null
  1706. },
  1707. /**
  1708. * Options for the navigator X axis. Default series options for the
  1709. * navigator xAxis are:
  1710. * ```js
  1711. * xAxis: {
  1712. * tickWidth: 0,
  1713. * lineWidth: 0,
  1714. * gridLineWidth: 1,
  1715. * tickPixelInterval: 200,
  1716. * labels: {
  1717. * align: 'left',
  1718. * style: {
  1719. * color: '#888'
  1720. * },
  1721. * x: 3,
  1722. * y: -4
  1723. * }
  1724. * }
  1725. * ```
  1726. *
  1727. * @extends xAxis
  1728. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  1729. * showEmpty, maxRange
  1730. */
  1731. xAxis: {
  1732. /**
  1733. * Additional range on the right side of the xAxis. Works similar to
  1734. * xAxis.maxPadding, but value is set in milliseconds.
  1735. * Can be set for both, main xAxis and navigator's xAxis.
  1736. *
  1737. * @since 6.0.0
  1738. */
  1739. overscroll: 0,
  1740. className: 'highcharts-navigator-xaxis',
  1741. tickLength: 0,
  1742. lineWidth: 0,
  1743. gridLineColor: Palette.neutralColor10,
  1744. gridLineWidth: 1,
  1745. tickPixelInterval: 200,
  1746. labels: {
  1747. align: 'left',
  1748. /**
  1749. * @type {Highcharts.CSSObject}
  1750. */
  1751. style: {
  1752. /** @ignore */
  1753. color: Palette.neutralColor40
  1754. },
  1755. x: 3,
  1756. y: -4
  1757. },
  1758. crosshair: false
  1759. },
  1760. /**
  1761. * Options for the navigator Y axis. Default series options for the
  1762. * navigator yAxis are:
  1763. * ```js
  1764. * yAxis: {
  1765. * gridLineWidth: 0,
  1766. * startOnTick: false,
  1767. * endOnTick: false,
  1768. * minPadding: 0.1,
  1769. * maxPadding: 0.1,
  1770. * labels: {
  1771. * enabled: false
  1772. * },
  1773. * title: {
  1774. * text: null
  1775. * },
  1776. * tickWidth: 0
  1777. * }
  1778. * ```
  1779. *
  1780. * @extends yAxis
  1781. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  1782. * showEmpty, scrollbar, top, units, maxRange, minLength,
  1783. * maxLength, resize
  1784. */
  1785. yAxis: {
  1786. className: 'highcharts-navigator-yaxis',
  1787. gridLineWidth: 0,
  1788. startOnTick: false,
  1789. endOnTick: false,
  1790. minPadding: 0.1,
  1791. maxPadding: 0.1,
  1792. labels: {
  1793. enabled: false
  1794. },
  1795. crosshair: false,
  1796. title: {
  1797. text: null
  1798. },
  1799. tickLength: 0,
  1800. tickWidth: 0
  1801. }
  1802. }
  1803. });
  1804. /* eslint-disable no-invalid-this, valid-jsdoc */
  1805. /**
  1806. * Draw one of the handles on the side of the zoomed range in the navigator
  1807. *
  1808. * @private
  1809. * @function Highcharts.Renderer#symbols.navigator-handle
  1810. * @param {number} x
  1811. * @param {number} y
  1812. * @param {number} w
  1813. * @param {number} h
  1814. * @param {Highcharts.NavigatorHandlesOptions} options
  1815. * @return {Highcharts.SVGPathArray}
  1816. * Path to be used in a handle
  1817. */
  1818. RendererRegistry.getRendererType().prototype.symbols['navigator-handle'] = function (_x, _y, _w, _h, options) {
  1819. var halfWidth = (options && options.width || 0) / 2,
  1820. markerPosition = Math.round(halfWidth / 3) + 0.5,
  1821. height = options && options.height || 0;
  1822. return [
  1823. ['M', -halfWidth - 1, 0.5],
  1824. ['L', halfWidth, 0.5],
  1825. ['L', halfWidth, height + 0.5],
  1826. ['L', -halfWidth - 1, height + 0.5],
  1827. ['L', -halfWidth - 1, 0.5],
  1828. ['M', -markerPosition, 4],
  1829. ['L', -markerPosition, height - 3],
  1830. ['M', markerPosition - 1, 4],
  1831. ['L', markerPosition - 1, height - 3]
  1832. ];
  1833. };
  1834. /**
  1835. * The Navigator class
  1836. *
  1837. * @private
  1838. * @class
  1839. * @name Highcharts.Navigator
  1840. *
  1841. * @param {Highcharts.Chart} chart
  1842. * Chart object
  1843. */
  1844. var Navigator = /** @class */ (function () {
  1845. function Navigator(chart) {
  1846. this.baseSeries = void 0;
  1847. this.chart = void 0;
  1848. this.handles = void 0;
  1849. this.height = void 0;
  1850. this.left = void 0;
  1851. this.navigatorEnabled = void 0;
  1852. this.navigatorGroup = void 0;
  1853. this.navigatorOptions = void 0;
  1854. this.navigatorSeries = void 0;
  1855. this.navigatorSize = void 0;
  1856. this.opposite = void 0;
  1857. this.outline = void 0;
  1858. this.outlineHeight = void 0;
  1859. this.range = void 0;
  1860. this.rendered = void 0;
  1861. this.shades = void 0;
  1862. this.size = void 0;
  1863. this.top = void 0;
  1864. this.xAxis = void 0;
  1865. this.yAxis = void 0;
  1866. this.zoomedMax = void 0;
  1867. this.zoomedMin = void 0;
  1868. this.init(chart);
  1869. }
  1870. /**
  1871. * Draw one of the handles on the side of the zoomed range in the navigator
  1872. *
  1873. * @private
  1874. * @function Highcharts.Navigator#drawHandle
  1875. *
  1876. * @param {number} x
  1877. * The x center for the handle
  1878. *
  1879. * @param {number} index
  1880. * 0 for left and 1 for right
  1881. *
  1882. * @param {boolean|undefined} inverted
  1883. * flag for chart.inverted
  1884. *
  1885. * @param {string} verb
  1886. * use 'animate' or 'attr'
  1887. */
  1888. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  1889. var navigator = this,
  1890. height = navigator.navigatorOptions.handles.height;
  1891. // Place it
  1892. navigator.handles[index][verb](inverted ? {
  1893. translateX: Math.round(navigator.left + navigator.height / 2),
  1894. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  1895. } : {
  1896. translateX: Math.round(navigator.left + parseInt(x, 10)),
  1897. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  1898. });
  1899. };
  1900. /**
  1901. * Render outline around the zoomed range
  1902. *
  1903. * @private
  1904. * @function Highcharts.Navigator#drawOutline
  1905. *
  1906. * @param {number} zoomedMin
  1907. * in pixels position where zoomed range starts
  1908. *
  1909. * @param {number} zoomedMax
  1910. * in pixels position where zoomed range ends
  1911. *
  1912. * @param {boolean|undefined} inverted
  1913. * flag if chart is inverted
  1914. *
  1915. * @param {string} verb
  1916. * use 'animate' or 'attr'
  1917. */
  1918. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  1919. var navigator = this,
  1920. maskInside = navigator.navigatorOptions.maskInside,
  1921. outlineWidth = navigator.outline.strokeWidth(),
  1922. halfOutline = outlineWidth / 2,
  1923. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  1924. outlineHeight = navigator.outlineHeight,
  1925. scrollbarHeight = navigator.scrollbarHeight || 0,
  1926. navigatorSize = navigator.size,
  1927. left = navigator.left - scrollbarHeight,
  1928. navigatorTop = navigator.top,
  1929. verticalMin,
  1930. path;
  1931. if (inverted) {
  1932. left -= halfOutline;
  1933. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  1934. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  1935. path = [
  1936. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  1937. ['L', left + outlineHeight, verticalMin],
  1938. ['L', left, verticalMin],
  1939. ['L', left, zoomedMax],
  1940. ['L', left + outlineHeight, zoomedMax],
  1941. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  1942. ];
  1943. if (maskInside) {
  1944. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  1945. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  1946. );
  1947. }
  1948. }
  1949. else {
  1950. zoomedMin += left + scrollbarHeight - outlineCorrection;
  1951. zoomedMax += left + scrollbarHeight - outlineCorrection;
  1952. navigatorTop += halfOutline;
  1953. path = [
  1954. ['M', left, navigatorTop],
  1955. ['L', zoomedMin, navigatorTop],
  1956. ['L', zoomedMin, navigatorTop + outlineHeight],
  1957. ['L', zoomedMax, navigatorTop + outlineHeight],
  1958. ['L', zoomedMax, navigatorTop],
  1959. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  1960. ];
  1961. if (maskInside) {
  1962. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  1963. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  1964. );
  1965. }
  1966. }
  1967. navigator.outline[verb]({
  1968. d: path
  1969. });
  1970. };
  1971. /**
  1972. * Render outline around the zoomed range
  1973. *
  1974. * @private
  1975. * @function Highcharts.Navigator#drawMasks
  1976. *
  1977. * @param {number} zoomedMin
  1978. * in pixels position where zoomed range starts
  1979. *
  1980. * @param {number} zoomedMax
  1981. * in pixels position where zoomed range ends
  1982. *
  1983. * @param {boolean|undefined} inverted
  1984. * flag if chart is inverted
  1985. *
  1986. * @param {string} verb
  1987. * use 'animate' or 'attr'
  1988. */
  1989. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  1990. var navigator = this,
  1991. left = navigator.left,
  1992. top = navigator.top,
  1993. navigatorHeight = navigator.height,
  1994. height,
  1995. width,
  1996. x,
  1997. y;
  1998. // Determine rectangle position & size
  1999. // According to (non)inverted position:
  2000. if (inverted) {
  2001. x = [left, left, left];
  2002. y = [top, top + zoomedMin, top + zoomedMax];
  2003. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  2004. height = [
  2005. zoomedMin,
  2006. zoomedMax - zoomedMin,
  2007. navigator.size - zoomedMax
  2008. ];
  2009. }
  2010. else {
  2011. x = [left, left + zoomedMin, left + zoomedMax];
  2012. y = [top, top, top];
  2013. width = [
  2014. zoomedMin,
  2015. zoomedMax - zoomedMin,
  2016. navigator.size - zoomedMax
  2017. ];
  2018. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  2019. }
  2020. navigator.shades.forEach(function (shade, i) {
  2021. shade[verb]({
  2022. x: x[i],
  2023. y: y[i],
  2024. width: width[i],
  2025. height: height[i]
  2026. });
  2027. });
  2028. };
  2029. /**
  2030. * Generate DOM elements for a navigator:
  2031. *
  2032. * - main navigator group
  2033. *
  2034. * - all shades
  2035. *
  2036. * - outline
  2037. *
  2038. * - handles
  2039. *
  2040. * @private
  2041. * @function Highcharts.Navigator#renderElements
  2042. */
  2043. Navigator.prototype.renderElements = function () {
  2044. var navigator = this,
  2045. navigatorOptions = navigator.navigatorOptions,
  2046. maskInside = navigatorOptions.maskInside,
  2047. chart = navigator.chart,
  2048. inverted = chart.inverted,
  2049. renderer = chart.renderer,
  2050. navigatorGroup,
  2051. mouseCursor = {
  2052. cursor: inverted ? 'ns-resize' : 'ew-resize'
  2053. };
  2054. // Create the main navigator group
  2055. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  2056. .attr({
  2057. zIndex: 8,
  2058. visibility: 'hidden'
  2059. })
  2060. .add();
  2061. // Create masks, each mask will get events and fill:
  2062. [
  2063. !maskInside,
  2064. maskInside,
  2065. !maskInside
  2066. ].forEach(function (hasMask, index) {
  2067. navigator.shades[index] = renderer.rect()
  2068. .addClass('highcharts-navigator-mask' +
  2069. (index === 1 ? '-inside' : '-outside'))
  2070. .add(navigatorGroup);
  2071. if (!chart.styledMode) {
  2072. navigator.shades[index]
  2073. .attr({
  2074. fill: hasMask ?
  2075. navigatorOptions.maskFill :
  2076. 'rgba(0,0,0,0)'
  2077. })
  2078. .css((index === 1) && mouseCursor);
  2079. }
  2080. });
  2081. // Create the outline:
  2082. navigator.outline = renderer.path()
  2083. .addClass('highcharts-navigator-outline')
  2084. .add(navigatorGroup);
  2085. if (!chart.styledMode) {
  2086. navigator.outline.attr({
  2087. 'stroke-width': navigatorOptions.outlineWidth,
  2088. stroke: navigatorOptions.outlineColor
  2089. });
  2090. }
  2091. // Create the handlers:
  2092. if (navigatorOptions.handles.enabled) {
  2093. [0, 1].forEach(function (index) {
  2094. navigatorOptions.handles.inverted = chart.inverted;
  2095. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  2096. // zIndex = 6 for right handle, 7 for left.
  2097. // Can't be 10, because of the tooltip in inverted chart #2908
  2098. navigator.handles[index].attr({ zIndex: 7 - index })
  2099. .addClass('highcharts-navigator-handle ' +
  2100. 'highcharts-navigator-handle-' +
  2101. ['left', 'right'][index]).add(navigatorGroup);
  2102. if (!chart.styledMode) {
  2103. var handlesOptions = navigatorOptions.handles;
  2104. navigator.handles[index]
  2105. .attr({
  2106. fill: handlesOptions.backgroundColor,
  2107. stroke: handlesOptions.borderColor,
  2108. 'stroke-width': handlesOptions.lineWidth
  2109. })
  2110. .css(mouseCursor);
  2111. }
  2112. });
  2113. }
  2114. };
  2115. /**
  2116. * Update navigator
  2117. *
  2118. * @private
  2119. * @function Highcharts.Navigator#update
  2120. *
  2121. * @param {Highcharts.NavigatorOptions} options
  2122. * Options to merge in when updating navigator
  2123. */
  2124. Navigator.prototype.update = function (options) {
  2125. // Remove references to old navigator series in base series
  2126. (this.series || []).forEach(function (series) {
  2127. if (series.baseSeries) {
  2128. delete series.baseSeries.navigatorSeries;
  2129. }
  2130. });
  2131. // Destroy and rebuild navigator
  2132. this.destroy();
  2133. var chartOptions = this.chart.options;
  2134. merge(true, chartOptions.navigator, this.options, options);
  2135. this.init(this.chart);
  2136. };
  2137. /**
  2138. * Render the navigator
  2139. *
  2140. * @private
  2141. * @function Highcharts.Navigator#render
  2142. * @param {number} min
  2143. * X axis value minimum
  2144. * @param {number} max
  2145. * X axis value maximum
  2146. * @param {number} [pxMin]
  2147. * Pixel value minimum
  2148. * @param {number} [pxMax]
  2149. * Pixel value maximum
  2150. * @return {void}
  2151. */
  2152. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  2153. var navigator = this,
  2154. chart = navigator.chart,
  2155. navigatorWidth,
  2156. scrollbarLeft,
  2157. scrollbarTop,
  2158. scrollbarHeight = navigator.scrollbarHeight,
  2159. navigatorSize,
  2160. xAxis = navigator.xAxis,
  2161. pointRange = xAxis.pointRange || 0,
  2162. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  2163. navigatorEnabled = navigator.navigatorEnabled,
  2164. zoomedMin,
  2165. zoomedMax,
  2166. rendered = navigator.rendered,
  2167. inverted = chart.inverted,
  2168. verb,
  2169. newMin,
  2170. newMax,
  2171. currentRange,
  2172. minRange = chart.xAxis[0].minRange,
  2173. maxRange = chart.xAxis[0].options.maxRange;
  2174. // Don't redraw while moving the handles (#4703).
  2175. if (this.hasDragged && !defined(pxMin)) {
  2176. return;
  2177. }
  2178. min = correctFloat(min - pointRange / 2);
  2179. max = correctFloat(max + pointRange / 2);
  2180. // Don't render the navigator until we have data (#486, #4202, #5172).
  2181. if (!isNumber(min) || !isNumber(max)) {
  2182. // However, if navigator was already rendered, we may need to resize
  2183. // it. For example hidden series, but visible navigator (#6022).
  2184. if (rendered) {
  2185. pxMin = 0;
  2186. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  2187. }
  2188. else {
  2189. return;
  2190. }
  2191. }
  2192. navigator.left = pick(xAxis.left,
  2193. // in case of scrollbar only, without navigator
  2194. chart.plotLeft + scrollbarHeight +
  2195. (inverted ? chart.plotWidth : 0));
  2196. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  2197. 2 * scrollbarHeight);
  2198. if (inverted) {
  2199. navigatorWidth = scrollbarHeight;
  2200. }
  2201. else {
  2202. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  2203. }
  2204. // Get the pixel position of the handles
  2205. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  2206. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  2207. // Verify (#1851, #2238)
  2208. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  2209. pxMin = 0;
  2210. pxMax = navigatorWidth;
  2211. }
  2212. // Are we below the minRange? (#2618, #6191)
  2213. newMin = xAxis.toValue(pxMin, true);
  2214. newMax = xAxis.toValue(pxMax, true);
  2215. currentRange = Math.abs(correctFloat(newMax - newMin));
  2216. if (currentRange < minRange) {
  2217. if (this.grabbedLeft) {
  2218. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  2219. }
  2220. else if (this.grabbedRight) {
  2221. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  2222. }
  2223. }
  2224. else if (defined(maxRange) &&
  2225. correctFloat(currentRange - pointRange) > maxRange) {
  2226. if (this.grabbedLeft) {
  2227. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  2228. }
  2229. else if (this.grabbedRight) {
  2230. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  2231. }
  2232. }
  2233. // Handles are allowed to cross, but never exceed the plot area
  2234. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  2235. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  2236. navigator.zoomedMax - navigator.fixedWidth :
  2237. Math.min(pxMin, pxMax), 0, zoomedMax);
  2238. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  2239. zoomedMax = Math.round(navigator.zoomedMax);
  2240. zoomedMin = Math.round(navigator.zoomedMin);
  2241. if (navigatorEnabled) {
  2242. navigator.navigatorGroup.attr({
  2243. visibility: 'visible'
  2244. });
  2245. // Place elements
  2246. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  2247. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2248. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2249. if (navigator.navigatorOptions.handles.enabled) {
  2250. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2251. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2252. }
  2253. }
  2254. if (navigator.scrollbar) {
  2255. if (inverted) {
  2256. scrollbarTop = navigator.top - scrollbarHeight;
  2257. scrollbarLeft = navigator.left - scrollbarHeight +
  2258. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  2259. // Multiple axes has offsets:
  2260. (scrollbarXAxis.titleOffset || 0) +
  2261. // Self margin from the axis.title
  2262. scrollbarXAxis.axisTitleMargin);
  2263. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  2264. }
  2265. else {
  2266. scrollbarTop = navigator.top + (navigatorEnabled ?
  2267. navigator.height :
  2268. -scrollbarHeight);
  2269. scrollbarLeft = navigator.left - scrollbarHeight;
  2270. }
  2271. // Reposition scrollbar
  2272. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  2273. // Keep scale 0-1
  2274. navigator.scrollbar.setRange(
  2275. // Use real value, not rounded because range can be very small
  2276. // (#1716)
  2277. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  2278. }
  2279. navigator.rendered = true;
  2280. };
  2281. /**
  2282. * Set up the mouse and touch events for the navigator
  2283. *
  2284. * @private
  2285. * @function Highcharts.Navigator#addMouseEvents
  2286. */
  2287. Navigator.prototype.addMouseEvents = function () {
  2288. var navigator = this,
  2289. chart = navigator.chart,
  2290. container = chart.container,
  2291. eventsToUnbind = [],
  2292. mouseMoveHandler,
  2293. mouseUpHandler;
  2294. /**
  2295. * Create mouse events' handlers.
  2296. * Make them as separate functions to enable wrapping them:
  2297. */
  2298. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  2299. navigator.onMouseMove(e);
  2300. };
  2301. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  2302. navigator.onMouseUp(e);
  2303. };
  2304. // Add shades and handles mousedown events
  2305. eventsToUnbind = navigator.getPartsEvents('mousedown');
  2306. // Add mouse move and mouseup events. These are bind to doc/container,
  2307. // because Navigator.grabbedSomething flags are stored in mousedown
  2308. // events
  2309. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  2310. // Touch events
  2311. if (hasTouch) {
  2312. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  2313. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  2314. }
  2315. navigator.eventsToUnbind = eventsToUnbind;
  2316. // Data events
  2317. if (navigator.series && navigator.series[0]) {
  2318. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  2319. chart.navigator.modifyNavigatorAxisExtremes();
  2320. }));
  2321. }
  2322. };
  2323. /**
  2324. * Generate events for handles and masks
  2325. *
  2326. * @private
  2327. * @function Highcharts.Navigator#getPartsEvents
  2328. *
  2329. * @param {string} eventName
  2330. * Event name handler, 'mousedown' or 'touchstart'
  2331. *
  2332. * @return {Array<Function>}
  2333. * An array of functions to remove navigator functions from the
  2334. * events again.
  2335. */
  2336. Navigator.prototype.getPartsEvents = function (eventName) {
  2337. var navigator = this,
  2338. events = [];
  2339. ['shades', 'handles'].forEach(function (name) {
  2340. navigator[name].forEach(function (navigatorItem, index) {
  2341. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  2342. navigator[name + 'Mousedown'](e, index);
  2343. }));
  2344. });
  2345. });
  2346. return events;
  2347. };
  2348. /**
  2349. * Mousedown on a shaded mask, either:
  2350. *
  2351. * - will be stored for future drag&drop
  2352. *
  2353. * - will directly shift to a new range
  2354. *
  2355. * @private
  2356. * @function Highcharts.Navigator#shadesMousedown
  2357. *
  2358. * @param {Highcharts.PointerEventObject} e
  2359. * Mouse event
  2360. *
  2361. * @param {number} index
  2362. * Index of a mask in Navigator.shades array
  2363. */
  2364. Navigator.prototype.shadesMousedown = function (e, index) {
  2365. e = this.chart.pointer.normalize(e);
  2366. var navigator = this,
  2367. chart = navigator.chart,
  2368. xAxis = navigator.xAxis,
  2369. zoomedMin = navigator.zoomedMin,
  2370. navigatorPosition = navigator.left,
  2371. navigatorSize = navigator.size,
  2372. range = navigator.range,
  2373. chartX = e.chartX,
  2374. fixedMax,
  2375. fixedMin,
  2376. ext,
  2377. left;
  2378. // For inverted chart, swap some options:
  2379. if (chart.inverted) {
  2380. chartX = e.chartY;
  2381. navigatorPosition = navigator.top;
  2382. }
  2383. if (index === 1) {
  2384. // Store information for drag&drop
  2385. navigator.grabbedCenter = chartX;
  2386. navigator.fixedWidth = range;
  2387. navigator.dragOffset = chartX - zoomedMin;
  2388. }
  2389. else {
  2390. // Shift the range by clicking on shaded areas
  2391. left = chartX - navigatorPosition - range / 2;
  2392. if (index === 0) {
  2393. left = Math.max(0, left);
  2394. }
  2395. else if (index === 2 && left + range >= navigatorSize) {
  2396. left = navigatorSize - range;
  2397. if (navigator.reversedExtremes) {
  2398. // #7713
  2399. left -= range;
  2400. fixedMin = navigator.getUnionExtremes().dataMin;
  2401. }
  2402. else {
  2403. // #2293, #3543
  2404. fixedMax = navigator.getUnionExtremes().dataMax;
  2405. }
  2406. }
  2407. if (left !== zoomedMin) { // it has actually moved
  2408. navigator.fixedWidth = range; // #1370
  2409. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  2410. if (defined(ext.min)) { // #7411
  2411. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  2412. { trigger: 'navigator' });
  2413. }
  2414. }
  2415. }
  2416. };
  2417. /**
  2418. * Mousedown on a handle mask.
  2419. * Will store necessary information for drag&drop.
  2420. *
  2421. * @private
  2422. * @function Highcharts.Navigator#handlesMousedown
  2423. * @param {Highcharts.PointerEventObject} e
  2424. * Mouse event
  2425. * @param {number} index
  2426. * Index of a handle in Navigator.handles array
  2427. * @return {void}
  2428. */
  2429. Navigator.prototype.handlesMousedown = function (e, index) {
  2430. e = this.chart.pointer.normalize(e);
  2431. var navigator = this,
  2432. chart = navigator.chart,
  2433. baseXAxis = chart.xAxis[0],
  2434. // For reversed axes, min and max are changed,
  2435. // so the other extreme should be stored
  2436. reverse = navigator.reversedExtremes;
  2437. if (index === 0) {
  2438. // Grab the left handle
  2439. navigator.grabbedLeft = true;
  2440. navigator.otherHandlePos = navigator.zoomedMax;
  2441. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  2442. }
  2443. else {
  2444. // Grab the right handle
  2445. navigator.grabbedRight = true;
  2446. navigator.otherHandlePos = navigator.zoomedMin;
  2447. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  2448. }
  2449. chart.fixedRange = null;
  2450. };
  2451. /**
  2452. * Mouse move event based on x/y mouse position.
  2453. *
  2454. * @private
  2455. * @function Highcharts.Navigator#onMouseMove
  2456. *
  2457. * @param {Highcharts.PointerEventObject} e
  2458. * Mouse event
  2459. */
  2460. Navigator.prototype.onMouseMove = function (e) {
  2461. var navigator = this,
  2462. chart = navigator.chart,
  2463. left = navigator.left,
  2464. navigatorSize = navigator.navigatorSize,
  2465. range = navigator.range,
  2466. dragOffset = navigator.dragOffset,
  2467. inverted = chart.inverted,
  2468. chartX;
  2469. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  2470. // the finger down in the center of the scrollbar. This should be
  2471. // ignored.
  2472. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  2473. e = chart.pointer.normalize(e);
  2474. chartX = e.chartX;
  2475. // Swap some options for inverted chart
  2476. if (inverted) {
  2477. left = navigator.top;
  2478. chartX = e.chartY;
  2479. }
  2480. // Drag left handle or top handle
  2481. if (navigator.grabbedLeft) {
  2482. navigator.hasDragged = true;
  2483. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  2484. // Drag right handle or bottom handle
  2485. }
  2486. else if (navigator.grabbedRight) {
  2487. navigator.hasDragged = true;
  2488. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  2489. // Drag scrollbar or open area in navigator
  2490. }
  2491. else if (navigator.grabbedCenter) {
  2492. navigator.hasDragged = true;
  2493. if (chartX < dragOffset) { // outside left
  2494. chartX = dragOffset;
  2495. // outside right
  2496. }
  2497. else if (chartX >
  2498. navigatorSize + dragOffset - range) {
  2499. chartX = navigatorSize + dragOffset - range;
  2500. }
  2501. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  2502. }
  2503. if (navigator.hasDragged &&
  2504. navigator.scrollbar &&
  2505. pick(navigator.scrollbar.options.liveRedraw,
  2506. // By default, don't run live redraw on VML, on touch
  2507. // devices or if the chart is in boost.
  2508. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  2509. e.DOMType = e.type; // DOMType is for IE8
  2510. setTimeout(function () {
  2511. navigator.onMouseUp(e);
  2512. }, 0);
  2513. }
  2514. }
  2515. };
  2516. /**
  2517. * Mouse up event based on x/y mouse position.
  2518. *
  2519. * @private
  2520. * @function Highcharts.Navigator#onMouseUp
  2521. * @param {Highcharts.PointerEventObject} e
  2522. * Mouse event
  2523. * @return {void}
  2524. */
  2525. Navigator.prototype.onMouseUp = function (e) {
  2526. var navigator = this,
  2527. chart = navigator.chart,
  2528. xAxis = navigator.xAxis,
  2529. scrollbar = navigator.scrollbar,
  2530. DOMEvent = e.DOMEvent || e,
  2531. inverted = chart.inverted,
  2532. verb = navigator.rendered && !navigator.hasDragged ?
  2533. 'animate' : 'attr',
  2534. zoomedMax,
  2535. zoomedMin,
  2536. unionExtremes,
  2537. fixedMin,
  2538. fixedMax,
  2539. ext;
  2540. if (
  2541. // MouseUp is called for both, navigator and scrollbar (that order),
  2542. // which causes calling afterSetExtremes twice. Prevent first call
  2543. // by checking if scrollbar is going to set new extremes (#6334)
  2544. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  2545. e.trigger === 'scrollbar') {
  2546. unionExtremes = navigator.getUnionExtremes();
  2547. // When dragging one handle, make sure the other one doesn't change
  2548. if (navigator.zoomedMin === navigator.otherHandlePos) {
  2549. fixedMin = navigator.fixedExtreme;
  2550. }
  2551. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  2552. fixedMax = navigator.fixedExtreme;
  2553. }
  2554. // Snap to right edge (#4076)
  2555. if (navigator.zoomedMax === navigator.size) {
  2556. fixedMax = navigator.reversedExtremes ?
  2557. unionExtremes.dataMin :
  2558. unionExtremes.dataMax;
  2559. }
  2560. // Snap to left edge (#7576)
  2561. if (navigator.zoomedMin === 0) {
  2562. fixedMin = navigator.reversedExtremes ?
  2563. unionExtremes.dataMax :
  2564. unionExtremes.dataMin;
  2565. }
  2566. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  2567. if (defined(ext.min)) {
  2568. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  2569. // Run animation when clicking buttons, scrollbar track etc,
  2570. // but not when dragging handles or scrollbar
  2571. navigator.hasDragged ? false : null, {
  2572. trigger: 'navigator',
  2573. triggerOp: 'navigator-drag',
  2574. DOMEvent: DOMEvent // #1838
  2575. });
  2576. }
  2577. }
  2578. if (e.DOMType !== 'mousemove' &&
  2579. e.DOMType !== 'touchmove') {
  2580. navigator.grabbedLeft = navigator.grabbedRight =
  2581. navigator.grabbedCenter = navigator.fixedWidth =
  2582. navigator.fixedExtreme = navigator.otherHandlePos =
  2583. navigator.hasDragged = navigator.dragOffset = null;
  2584. }
  2585. // Update position of navigator shades, outline and handles (#12573)
  2586. if (navigator.navigatorEnabled &&
  2587. isNumber(navigator.zoomedMin) &&
  2588. isNumber(navigator.zoomedMax)) {
  2589. zoomedMin = Math.round(navigator.zoomedMin);
  2590. zoomedMax = Math.round(navigator.zoomedMax);
  2591. if (navigator.shades) {
  2592. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2593. }
  2594. if (navigator.outline) {
  2595. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2596. }
  2597. if (navigator.navigatorOptions.handles.enabled &&
  2598. Object.keys(navigator.handles).length ===
  2599. navigator.handles.length) {
  2600. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2601. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2602. }
  2603. }
  2604. };
  2605. /**
  2606. * Removes the event handlers attached previously with addEvents.
  2607. *
  2608. * @private
  2609. * @function Highcharts.Navigator#removeEvents
  2610. * @return {void}
  2611. */
  2612. Navigator.prototype.removeEvents = function () {
  2613. if (this.eventsToUnbind) {
  2614. this.eventsToUnbind.forEach(function (unbind) {
  2615. unbind();
  2616. });
  2617. this.eventsToUnbind = void 0;
  2618. }
  2619. this.removeBaseSeriesEvents();
  2620. };
  2621. /**
  2622. * Remove data events.
  2623. *
  2624. * @private
  2625. * @function Highcharts.Navigator#removeBaseSeriesEvents
  2626. * @return {void}
  2627. */
  2628. Navigator.prototype.removeBaseSeriesEvents = function () {
  2629. var baseSeries = this.baseSeries || [];
  2630. if (this.navigatorEnabled && baseSeries[0]) {
  2631. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2632. baseSeries.forEach(function (series) {
  2633. removeEvent(series, 'updatedData', this.updatedDataHandler);
  2634. }, this);
  2635. }
  2636. // We only listen for extremes-events on the first baseSeries
  2637. if (baseSeries[0].xAxis) {
  2638. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2639. }
  2640. }
  2641. };
  2642. /**
  2643. * Initialize the Navigator object
  2644. *
  2645. * @private
  2646. * @function Highcharts.Navigator#init
  2647. *
  2648. * @param {Highcharts.Chart} chart
  2649. */
  2650. Navigator.prototype.init = function (chart) {
  2651. var chartOptions = chart.options,
  2652. navigatorOptions = chartOptions.navigator,
  2653. navigatorEnabled = navigatorOptions.enabled,
  2654. scrollbarOptions = chartOptions.scrollbar,
  2655. scrollbarEnabled = scrollbarOptions.enabled,
  2656. height = navigatorEnabled ? navigatorOptions.height : 0,
  2657. scrollbarHeight = scrollbarEnabled ?
  2658. scrollbarOptions.height :
  2659. 0;
  2660. this.handles = [];
  2661. this.shades = [];
  2662. this.chart = chart;
  2663. this.setBaseSeries();
  2664. this.height = height;
  2665. this.scrollbarHeight = scrollbarHeight;
  2666. this.scrollbarEnabled = scrollbarEnabled;
  2667. this.navigatorEnabled = navigatorEnabled;
  2668. this.navigatorOptions = navigatorOptions;
  2669. this.scrollbarOptions = scrollbarOptions;
  2670. this.outlineHeight = height + scrollbarHeight;
  2671. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  2672. var navigator = this,
  2673. baseSeries = navigator.baseSeries,
  2674. xAxisIndex = chart.xAxis.length,
  2675. yAxisIndex = chart.yAxis.length,
  2676. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  2677. chart.xAxis[0] || { options: {} };
  2678. chart.isDirtyBox = true;
  2679. if (navigator.navigatorEnabled) {
  2680. // an x axis is required for scrollbar also
  2681. navigator.xAxis = new Axis(chart, merge({
  2682. // inherit base xAxis' break and ordinal options
  2683. breaks: baseXaxis.options.breaks,
  2684. ordinal: baseXaxis.options.ordinal
  2685. }, navigatorOptions.xAxis, {
  2686. id: 'navigator-x-axis',
  2687. yAxis: 'navigator-y-axis',
  2688. isX: true,
  2689. type: 'datetime',
  2690. index: xAxisIndex,
  2691. isInternal: true,
  2692. offset: 0,
  2693. keepOrdinalPadding: true,
  2694. startOnTick: false,
  2695. endOnTick: false,
  2696. minPadding: 0,
  2697. maxPadding: 0,
  2698. zoomEnabled: false
  2699. }, chart.inverted ? {
  2700. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  2701. width: height
  2702. } : {
  2703. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  2704. height: height
  2705. }));
  2706. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  2707. id: 'navigator-y-axis',
  2708. alignTicks: false,
  2709. offset: 0,
  2710. index: yAxisIndex,
  2711. isInternal: true,
  2712. reversed: pick((navigatorOptions.yAxis && navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  2713. zoomEnabled: false
  2714. }, chart.inverted ? {
  2715. width: height
  2716. } : {
  2717. height: height
  2718. }));
  2719. // If we have a base series, initialize the navigator series
  2720. if (baseSeries || navigatorOptions.series.data) {
  2721. navigator.updateNavigatorSeries(false);
  2722. // If not, set up an event to listen for added series
  2723. }
  2724. else if (chart.series.length === 0) {
  2725. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  2726. // We've got one, now add it as base
  2727. if (chart.series.length > 0 && !navigator.series) {
  2728. navigator.setBaseSeries();
  2729. navigator.unbindRedraw(); // reset
  2730. }
  2731. });
  2732. }
  2733. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  2734. // Render items, so we can bind events to them:
  2735. navigator.renderElements();
  2736. // Add mouse events
  2737. navigator.addMouseEvents();
  2738. // in case of scrollbar only, fake an x axis to get translation
  2739. }
  2740. else {
  2741. navigator.xAxis = {
  2742. chart: chart,
  2743. navigatorAxis: {
  2744. fake: true
  2745. },
  2746. translate: function (value, reverse) {
  2747. 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;
  2748. return reverse ?
  2749. // from pixel to value
  2750. (value * valueRange / scrollTrackWidth) + min :
  2751. // from value to pixel
  2752. scrollTrackWidth * (value - min) / valueRange;
  2753. },
  2754. toPixels: function (value) {
  2755. return this.translate(value);
  2756. },
  2757. toValue: function (value) {
  2758. return this.translate(value, true);
  2759. }
  2760. };
  2761. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  2762. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  2763. }
  2764. // Initialize the scrollbar
  2765. if (chart.options.scrollbar.enabled) {
  2766. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  2767. margin: navigator.navigatorEnabled ? 0 : 10,
  2768. vertical: chart.inverted
  2769. }), chart);
  2770. addEvent(navigator.scrollbar, 'changed', function (e) {
  2771. var range = navigator.size,
  2772. to = range * this.to,
  2773. from = range * this.from;
  2774. navigator.hasDragged = navigator.scrollbar.hasDragged;
  2775. navigator.render(0, 0, from, to);
  2776. if (this.shouldUpdateExtremes(e.DOMType)) {
  2777. setTimeout(function () {
  2778. navigator.onMouseUp(e);
  2779. });
  2780. }
  2781. });
  2782. }
  2783. // Add data events
  2784. navigator.addBaseSeriesEvents();
  2785. // Add redraw events
  2786. navigator.addChartEvents();
  2787. };
  2788. /**
  2789. * Get the union data extremes of the chart - the outer data extremes of the
  2790. * base X axis and the navigator axis.
  2791. *
  2792. * @private
  2793. * @function Highcharts.Navigator#getUnionExtremes
  2794. * @param {boolean} [returnFalseOnNoBaseSeries]
  2795. * as the param says.
  2796. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  2797. */
  2798. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  2799. var baseAxis = this.chart.xAxis[0],
  2800. navAxis = this.xAxis,
  2801. navAxisOptions = navAxis.options,
  2802. baseAxisOptions = baseAxis.options,
  2803. ret;
  2804. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  2805. ret = {
  2806. dataMin: pick(// #4053
  2807. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  2808. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  2809. };
  2810. }
  2811. return ret;
  2812. };
  2813. /**
  2814. * Set the base series and update the navigator series from this. With a bit
  2815. * of modification we should be able to make this an API method to be called
  2816. * from the outside
  2817. *
  2818. * @private
  2819. * @function Highcharts.Navigator#setBaseSeries
  2820. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  2821. * Additional series options for a navigator
  2822. * @param {boolean} [redraw]
  2823. * Whether to redraw after update.
  2824. * @return {void}
  2825. */
  2826. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  2827. var chart = this.chart,
  2828. baseSeries = this.baseSeries = [];
  2829. baseSeriesOptions = (baseSeriesOptions ||
  2830. chart.options && chart.options.navigator.baseSeries ||
  2831. (chart.series.length ?
  2832. // Find the first non-navigator series (#8430)
  2833. find(chart.series, function (s) {
  2834. return !s.options.isInternal;
  2835. }).index :
  2836. 0));
  2837. // Iterate through series and add the ones that should be shown in
  2838. // navigator.
  2839. (chart.series || []).forEach(function (series, i) {
  2840. if (
  2841. // Don't include existing nav series
  2842. !series.options.isInternal &&
  2843. (series.options.showInNavigator ||
  2844. (i === baseSeriesOptions ||
  2845. series.options.id === baseSeriesOptions) &&
  2846. series.options.showInNavigator !== false)) {
  2847. baseSeries.push(series);
  2848. }
  2849. });
  2850. // When run after render, this.xAxis already exists
  2851. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  2852. this.updateNavigatorSeries(true, redraw);
  2853. }
  2854. };
  2855. /**
  2856. * Update series in the navigator from baseSeries, adding new if does not
  2857. * exist.
  2858. *
  2859. * @private
  2860. * @function Highcharts.Navigator.updateNavigatorSeries
  2861. * @param {boolean} addEvents
  2862. * @param {boolean} [redraw]
  2863. * @return {void}
  2864. */
  2865. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  2866. var navigator = this,
  2867. chart = navigator.chart,
  2868. baseSeries = navigator.baseSeries,
  2869. baseOptions,
  2870. mergedNavSeriesOptions,
  2871. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  2872. baseNavigatorOptions,
  2873. navSeriesMixin = {
  2874. enableMouseTracking: false,
  2875. index: null,
  2876. linkedTo: null,
  2877. group: 'nav',
  2878. padXAxis: false,
  2879. xAxis: 'navigator-x-axis',
  2880. yAxis: 'navigator-y-axis',
  2881. showInLegend: false,
  2882. stacking: void 0,
  2883. isInternal: true,
  2884. states: {
  2885. inactive: {
  2886. opacity: 1
  2887. }
  2888. }
  2889. },
  2890. // Remove navigator series that are no longer in the baseSeries
  2891. navigatorSeries = navigator.series =
  2892. (navigator.series || []).filter(function (navSeries) {
  2893. var base = navSeries.baseSeries;
  2894. if (baseSeries.indexOf(base) < 0) { // Not in array
  2895. // If there is still a base series connected to this
  2896. // series, remove event handler and reference.
  2897. if (base) {
  2898. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  2899. delete base.navigatorSeries;
  2900. }
  2901. // Kill the nav series. It may already have been
  2902. // destroyed (#8715).
  2903. if (navSeries.chart) {
  2904. navSeries.destroy();
  2905. }
  2906. return false;
  2907. }
  2908. return true;
  2909. });
  2910. // Go through each base series and merge the options to create new
  2911. // series
  2912. if (baseSeries && baseSeries.length) {
  2913. baseSeries.forEach(function eachBaseSeries(base) {
  2914. var linkedNavSeries = base.navigatorSeries,
  2915. userNavOptions = extend(
  2916. // Grab color and visibility from base as default
  2917. {
  2918. color: base.color,
  2919. visible: base.visible
  2920. }, !isArray(chartNavigatorSeriesOptions) ?
  2921. chartNavigatorSeriesOptions :
  2922. defaultOptions.navigator.series);
  2923. // Don't update if the series exists in nav and we have disabled
  2924. // adaptToUpdatedData.
  2925. if (linkedNavSeries &&
  2926. navigator.navigatorOptions.adaptToUpdatedData === false) {
  2927. return;
  2928. }
  2929. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  2930. baseOptions = base.options || {};
  2931. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  2932. // The dataLabels options are not merged correctly
  2933. // if the settings are an array, #13847.
  2934. userNavOptions.dataLabels = splat(userNavOptions.dataLabels);
  2935. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  2936. // Once nav series type is resolved, pick correct pointRange
  2937. mergedNavSeriesOptions.pointRange = pick(
  2938. // Stricte set pointRange in options
  2939. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  2940. // Fallback to default values, e.g. `null` for column
  2941. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  2942. // Merge data separately. Do a slice to avoid mutating the
  2943. // navigator options from base series (#4923).
  2944. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  2945. navigator.hasNavigatorData =
  2946. navigator.hasNavigatorData || !!navigatorSeriesData;
  2947. mergedNavSeriesOptions.data =
  2948. navigatorSeriesData ||
  2949. baseOptions.data && baseOptions.data.slice(0);
  2950. // Update or add the series
  2951. if (linkedNavSeries && linkedNavSeries.options) {
  2952. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  2953. }
  2954. else {
  2955. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  2956. base.navigatorSeries.baseSeries = base; // Store ref
  2957. navigatorSeries.push(base.navigatorSeries);
  2958. }
  2959. });
  2960. }
  2961. // If user has defined data (and no base series) or explicitly defined
  2962. // navigator.series as an array, we create these series on top of any
  2963. // base series.
  2964. if (chartNavigatorSeriesOptions.data &&
  2965. !(baseSeries && baseSeries.length) ||
  2966. isArray(chartNavigatorSeriesOptions)) {
  2967. navigator.hasNavigatorData = false;
  2968. // Allow navigator.series to be an array
  2969. chartNavigatorSeriesOptions =
  2970. splat(chartNavigatorSeriesOptions);
  2971. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  2972. navSeriesMixin.name =
  2973. 'Navigator ' + (navigatorSeries.length + 1);
  2974. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  2975. // Since we don't have a base series to pull color from,
  2976. // try to fake it by using color from series with same
  2977. // index. Otherwise pull from the colors array. We need
  2978. // an explicit color as otherwise updates will increment
  2979. // color counter and we'll get a new color for each
  2980. // update of the nav series.
  2981. color: chart.series[i] &&
  2982. !chart.series[i].options.isInternal &&
  2983. chart.series[i].color ||
  2984. chart.options.colors[i] ||
  2985. chart.options.colors[0]
  2986. }, navSeriesMixin, userSeriesOptions);
  2987. mergedNavSeriesOptions.data = userSeriesOptions.data;
  2988. if (mergedNavSeriesOptions.data) {
  2989. navigator.hasNavigatorData = true;
  2990. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  2991. }
  2992. });
  2993. }
  2994. if (addEvents) {
  2995. this.addBaseSeriesEvents();
  2996. }
  2997. };
  2998. /**
  2999. * Add data events.
  3000. * For example when main series is updated we need to recalculate extremes
  3001. *
  3002. * @private
  3003. * @function Highcharts.Navigator#addBaseSeriesEvent
  3004. * @return {void}
  3005. */
  3006. Navigator.prototype.addBaseSeriesEvents = function () {
  3007. var navigator = this,
  3008. baseSeries = navigator.baseSeries || [];
  3009. // Bind modified extremes event to first base's xAxis only.
  3010. // In event of > 1 base-xAxes, the navigator will ignore those.
  3011. // Adding this multiple times to the same axis is no problem, as
  3012. // duplicates should be discarded by the browser.
  3013. if (baseSeries[0] && baseSeries[0].xAxis) {
  3014. baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
  3015. }
  3016. baseSeries.forEach(function (base) {
  3017. // Link base series show/hide to navigator series visibility
  3018. base.eventsToUnbind.push(addEvent(base, 'show', function () {
  3019. if (this.navigatorSeries) {
  3020. this.navigatorSeries.setVisible(true, false);
  3021. }
  3022. }));
  3023. base.eventsToUnbind.push(addEvent(base, 'hide', function () {
  3024. if (this.navigatorSeries) {
  3025. this.navigatorSeries.setVisible(false, false);
  3026. }
  3027. }));
  3028. // Respond to updated data in the base series, unless explicitily
  3029. // not adapting to data changes.
  3030. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  3031. if (base.xAxis) {
  3032. base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));
  3033. }
  3034. }
  3035. // Handle series removal
  3036. base.eventsToUnbind.push(addEvent(base, 'remove', function () {
  3037. if (this.navigatorSeries) {
  3038. erase(navigator.series, this.navigatorSeries);
  3039. if (defined(this.navigatorSeries.options)) {
  3040. this.navigatorSeries.remove(false);
  3041. }
  3042. delete this.navigatorSeries;
  3043. }
  3044. }));
  3045. }, this);
  3046. };
  3047. /**
  3048. * Get minimum from all base series connected to the navigator
  3049. * @private
  3050. * @param {number} currentSeriesMin
  3051. * Minium from the current series
  3052. * @return {number} Minimum from all series
  3053. */
  3054. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  3055. return this.baseSeries.reduce(function (min, series) {
  3056. // (#10193)
  3057. return Math.min(min, series.xData ? series.xData[0] : min);
  3058. }, currentSeriesMin);
  3059. };
  3060. /**
  3061. * Set the navigator x axis extremes to reflect the total. The navigator
  3062. * extremes should always be the extremes of the union of all series in the
  3063. * chart as well as the navigator series.
  3064. *
  3065. * @private
  3066. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  3067. */
  3068. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  3069. var xAxis = this.xAxis,
  3070. unionExtremes;
  3071. if (typeof xAxis.getExtremes !== 'undefined') {
  3072. unionExtremes = this.getUnionExtremes(true);
  3073. if (unionExtremes &&
  3074. (unionExtremes.dataMin !== xAxis.min ||
  3075. unionExtremes.dataMax !== xAxis.max)) {
  3076. xAxis.min = unionExtremes.dataMin;
  3077. xAxis.max = unionExtremes.dataMax;
  3078. }
  3079. }
  3080. };
  3081. /**
  3082. * Hook to modify the base axis extremes with information from the Navigator
  3083. *
  3084. * @private
  3085. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  3086. */
  3087. Navigator.prototype.modifyBaseAxisExtremes = function () {
  3088. var baseXAxis = this,
  3089. navigator = baseXAxis.chart.navigator,
  3090. baseExtremes = baseXAxis.getExtremes(),
  3091. baseMin = baseExtremes.min,
  3092. baseMax = baseExtremes.max,
  3093. baseDataMin = baseExtremes.dataMin,
  3094. baseDataMax = baseExtremes.dataMax,
  3095. range = baseMax - baseMin,
  3096. stickToMin = navigator.stickToMin,
  3097. stickToMax = navigator.stickToMax,
  3098. overscroll = pick(baseXAxis.options.overscroll, 0),
  3099. newMax,
  3100. newMin,
  3101. navigatorSeries = navigator.series && navigator.series[0],
  3102. hasSetExtremes = !!baseXAxis.setExtremes,
  3103. // When the extremes have been set by range selector button, don't
  3104. // stick to min or max. The range selector buttons will handle the
  3105. // extremes. (#5489)
  3106. unmutable = baseXAxis.eventArgs &&
  3107. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  3108. if (!unmutable) {
  3109. // If the zoomed range is already at the min, move it to the right
  3110. // as new data comes in
  3111. if (stickToMin) {
  3112. newMin = baseDataMin;
  3113. newMax = newMin + range;
  3114. }
  3115. // If the zoomed range is already at the max, move it to the right
  3116. // as new data comes in
  3117. if (stickToMax) {
  3118. newMax = baseDataMax + overscroll;
  3119. // If stickToMin is true, the new min value is set above
  3120. if (!stickToMin) {
  3121. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  3122. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  3123. navigatorSeries.xData[0] :
  3124. -Number.MAX_VALUE));
  3125. }
  3126. }
  3127. // Update the extremes
  3128. if (hasSetExtremes && (stickToMin || stickToMax)) {
  3129. if (isNumber(newMin)) {
  3130. baseXAxis.min = baseXAxis.userMin = newMin;
  3131. baseXAxis.max = baseXAxis.userMax = newMax;
  3132. }
  3133. }
  3134. }
  3135. // Reset
  3136. navigator.stickToMin =
  3137. navigator.stickToMax = null;
  3138. };
  3139. /**
  3140. * Handler for updated data on the base series. When data is modified, the
  3141. * navigator series must reflect it. This is called from the Chart.redraw
  3142. * function before axis and series extremes are computed.
  3143. *
  3144. * @private
  3145. * @function Highcharts.Navigator#updateDataHandler
  3146. */
  3147. Navigator.prototype.updatedDataHandler = function () {
  3148. var navigator = this.chart.navigator,
  3149. baseSeries = this,
  3150. navigatorSeries = this.navigatorSeries;
  3151. // If the scrollbar is scrolled all the way to the right, keep right as
  3152. // new data comes in.
  3153. navigator.stickToMax = navigator.reversedExtremes ?
  3154. Math.round(navigator.zoomedMin) === 0 :
  3155. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  3156. navigator.stickToMin = navigator.shouldStickToMin(baseSeries, navigator);
  3157. // Set the navigator series data to the new data of the base series
  3158. if (navigatorSeries && !navigator.hasNavigatorData) {
  3159. navigatorSeries.options.pointStart = baseSeries.xData[0];
  3160. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  3161. }
  3162. };
  3163. /**
  3164. * Detect if the zoomed area should stick to the minimum, #14742.
  3165. *
  3166. * @private
  3167. * @function Highcharts.Navigator#shouldStickToMin
  3168. */
  3169. Navigator.prototype.shouldStickToMin = function (baseSeries, navigator) {
  3170. var xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]),
  3171. xAxis = baseSeries.xAxis,
  3172. max = xAxis.max,
  3173. min = xAxis.min,
  3174. range = xAxis.options.range;
  3175. var stickToMin = true;
  3176. if (isNumber(max) && isNumber(min)) {
  3177. // If range declared, stick to the minimum only if the range
  3178. // is smaller than the data set range.
  3179. if (range && max - xDataMin > 0) {
  3180. stickToMin = max - xDataMin < range && (!this.chart.fixedRange);
  3181. }
  3182. else {
  3183. // If the current axis minimum falls outside the new
  3184. // updated dataset, we must adjust.
  3185. stickToMin = min <= xDataMin;
  3186. }
  3187. }
  3188. return stickToMin;
  3189. };
  3190. /**
  3191. * Add chart events, like redrawing navigator, when chart requires that.
  3192. *
  3193. * @private
  3194. * @function Highcharts.Navigator#addChartEvents
  3195. * @return {void}
  3196. */
  3197. Navigator.prototype.addChartEvents = function () {
  3198. if (!this.eventsToUnbind) {
  3199. this.eventsToUnbind = [];
  3200. }
  3201. this.eventsToUnbind.push(
  3202. // Move the scrollbar after redraw, like after data updata even if
  3203. // axes don't redraw
  3204. addEvent(this.chart, 'redraw', function () {
  3205. var navigator = this.navigator,
  3206. xAxis = navigator && (navigator.baseSeries &&
  3207. navigator.baseSeries[0] &&
  3208. navigator.baseSeries[0].xAxis ||
  3209. this.xAxis[0]); // #5709, #13114
  3210. if (xAxis) {
  3211. navigator.render(xAxis.min,
  3212. xAxis.max);
  3213. }
  3214. }),
  3215. // Make room for the navigator, can be placed around the chart:
  3216. addEvent(this.chart, 'getMargins', function () {
  3217. var chart = this,
  3218. navigator = chart.navigator,
  3219. marginName = navigator.opposite ?
  3220. 'plotTop' : 'marginBottom';
  3221. if (chart.inverted) {
  3222. marginName = navigator.opposite ?
  3223. 'marginRight' : 'plotLeft';
  3224. }
  3225. chart[marginName] =
  3226. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  3227. navigator.outlineHeight :
  3228. 0) + navigator.navigatorOptions.margin;
  3229. }));
  3230. };
  3231. /**
  3232. * Destroys allocated elements.
  3233. *
  3234. * @private
  3235. * @function Highcharts.Navigator#destroy
  3236. */
  3237. Navigator.prototype.destroy = function () {
  3238. // Disconnect events added in addEvents
  3239. this.removeEvents();
  3240. if (this.xAxis) {
  3241. erase(this.chart.xAxis, this.xAxis);
  3242. erase(this.chart.axes, this.xAxis);
  3243. }
  3244. if (this.yAxis) {
  3245. erase(this.chart.yAxis, this.yAxis);
  3246. erase(this.chart.axes, this.yAxis);
  3247. }
  3248. // Destroy series
  3249. (this.series || []).forEach(function (s) {
  3250. if (s.destroy) {
  3251. s.destroy();
  3252. }
  3253. });
  3254. // Destroy properties
  3255. [
  3256. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  3257. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  3258. 'rendered'
  3259. ].forEach(function (prop) {
  3260. if (this[prop] && this[prop].destroy) {
  3261. this[prop].destroy();
  3262. }
  3263. this[prop] = null;
  3264. }, this);
  3265. // Destroy elements in collection
  3266. [this.handles].forEach(function (coll) {
  3267. destroyObjectProperties(coll);
  3268. }, this);
  3269. };
  3270. return Navigator;
  3271. }());
  3272. // End of prototype
  3273. if (!H.Navigator) {
  3274. H.Navigator = Navigator;
  3275. NavigatorAxis.compose(Axis);
  3276. // For Stock charts. For x only zooming, do not to create the zoom button
  3277. // because X axis zooming is already allowed by the Navigator and Range
  3278. // selector. (#9285)
  3279. addEvent(Chart, 'beforeShowResetZoom', function () {
  3280. var chartOptions = this.options,
  3281. navigator = chartOptions.navigator,
  3282. rangeSelector = chartOptions.rangeSelector;
  3283. if (((navigator && navigator.enabled) ||
  3284. (rangeSelector && rangeSelector.enabled)) &&
  3285. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  3286. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  3287. return false;
  3288. }
  3289. });
  3290. // Initialize navigator for stock charts
  3291. addEvent(Chart, 'beforeRender', function () {
  3292. var options = this.options;
  3293. if (options.navigator.enabled ||
  3294. options.scrollbar.enabled) {
  3295. this.scroller = this.navigator = new Navigator(this);
  3296. }
  3297. });
  3298. // For stock charts, extend the Chart.setChartSize method so that we can set
  3299. // the final top position of the navigator once the height of the chart,
  3300. // including the legend, is determined. #367. We can't use Chart.getMargins,
  3301. // because labels offsets are not calculated yet.
  3302. addEvent(Chart, 'afterSetChartSize', function () {
  3303. var legend = this.legend,
  3304. navigator = this.navigator,
  3305. scrollbarHeight,
  3306. legendOptions,
  3307. xAxis,
  3308. yAxis;
  3309. if (navigator) {
  3310. legendOptions = legend && legend.options;
  3311. xAxis = navigator.xAxis;
  3312. yAxis = navigator.yAxis;
  3313. scrollbarHeight = navigator.scrollbarHeight;
  3314. // Compute the top position
  3315. if (this.inverted) {
  3316. navigator.left = navigator.opposite ?
  3317. this.chartWidth - scrollbarHeight -
  3318. navigator.height :
  3319. this.spacing[3] + scrollbarHeight;
  3320. navigator.top = this.plotTop + scrollbarHeight;
  3321. }
  3322. else {
  3323. navigator.left = pick(xAxis.left, this.plotLeft + scrollbarHeight);
  3324. navigator.top = navigator.navigatorOptions.top ||
  3325. this.chartHeight -
  3326. navigator.height -
  3327. scrollbarHeight -
  3328. this.spacing[2] -
  3329. (this.rangeSelector && this.extraBottomMargin ?
  3330. this.rangeSelector.getHeight() :
  3331. 0) -
  3332. ((legendOptions &&
  3333. legendOptions.verticalAlign === 'bottom' &&
  3334. legendOptions.layout !== 'proximate' && // #13392
  3335. legendOptions.enabled &&
  3336. !legendOptions.floating) ?
  3337. legend.legendHeight +
  3338. pick(legendOptions.margin, 10) :
  3339. 0) -
  3340. (this.titleOffset ? this.titleOffset[2] : 0);
  3341. }
  3342. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  3343. if (this.inverted) {
  3344. xAxis.options.left = yAxis.options.left = navigator.left;
  3345. }
  3346. else {
  3347. xAxis.options.top = yAxis.options.top = navigator.top;
  3348. }
  3349. xAxis.setAxisSize();
  3350. yAxis.setAxisSize();
  3351. }
  3352. }
  3353. });
  3354. // Merge options, if no scrolling exists yet
  3355. addEvent(Chart, 'update', function (e) {
  3356. var navigatorOptions = (e.options.navigator || {}),
  3357. scrollbarOptions = (e.options.scrollbar || {});
  3358. if (!this.navigator && !this.scroller &&
  3359. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  3360. merge(true, this.options.navigator, navigatorOptions);
  3361. merge(true, this.options.scrollbar, scrollbarOptions);
  3362. delete e.options.navigator;
  3363. delete e.options.scrollbar;
  3364. }
  3365. });
  3366. // Initialize navigator, if no scrolling exists yet
  3367. addEvent(Chart, 'afterUpdate', function (event) {
  3368. if (!this.navigator && !this.scroller &&
  3369. (this.options.navigator.enabled ||
  3370. this.options.scrollbar.enabled)) {
  3371. this.scroller = this.navigator = new Navigator(this);
  3372. if (pick(event.redraw, true)) {
  3373. this.redraw(event.animation); // #7067
  3374. }
  3375. }
  3376. });
  3377. // Handle adding new series
  3378. addEvent(Chart, 'afterAddSeries', function () {
  3379. if (this.navigator) {
  3380. // Recompute which series should be shown in navigator, and add them
  3381. this.navigator.setBaseSeries(null, false);
  3382. }
  3383. });
  3384. // Handle updating series
  3385. addEvent(Series, 'afterUpdate', function () {
  3386. if (this.chart.navigator && !this.options.isInternal) {
  3387. this.chart.navigator.setBaseSeries(null, false);
  3388. }
  3389. });
  3390. Chart.prototype.callbacks.push(function (chart) {
  3391. var extremes,
  3392. navigator = chart.navigator;
  3393. // Initialize the navigator
  3394. if (navigator && chart.xAxis[0]) {
  3395. extremes = chart.xAxis[0].getExtremes();
  3396. navigator.render(extremes.min, extremes.max);
  3397. }
  3398. });
  3399. }
  3400. H.Navigator = Navigator;
  3401. return H.Navigator;
  3402. });
  3403. _registerModule(_modules, 'Core/Axis/OrdinalAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, Series, U) {
  3404. /* *
  3405. *
  3406. * (c) 2010-2021 Torstein Honsi
  3407. *
  3408. * License: www.highcharts.com/license
  3409. *
  3410. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3411. *
  3412. * */
  3413. var addEvent = U.addEvent,
  3414. css = U.css,
  3415. defined = U.defined,
  3416. error = U.error,
  3417. pick = U.pick,
  3418. timeUnits = U.timeUnits;
  3419. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  3420. /**
  3421. * Extends the axis with ordinal support.
  3422. * @private
  3423. */
  3424. var OrdinalAxis;
  3425. (function (OrdinalAxis) {
  3426. /* *
  3427. *
  3428. * Classes
  3429. *
  3430. * */
  3431. /**
  3432. * @private
  3433. */
  3434. var Composition = /** @class */ (function () {
  3435. /* *
  3436. *
  3437. * Constructors
  3438. *
  3439. * */
  3440. /**
  3441. * @private
  3442. */
  3443. function Composition(axis) {
  3444. this.index = {};
  3445. this.axis = axis;
  3446. }
  3447. /* *
  3448. *
  3449. * Functions
  3450. *
  3451. * */
  3452. /**
  3453. * Calculate the ordinal positions before tick positions are calculated.
  3454. *
  3455. * @private
  3456. */
  3457. Composition.prototype.beforeSetTickPositions = function () {
  3458. var axis = this.axis,
  3459. ordinal = axis.ordinal,
  3460. len,
  3461. ordinalPositions = [],
  3462. uniqueOrdinalPositions,
  3463. useOrdinal = false,
  3464. dist,
  3465. extremes = axis.getExtremes(),
  3466. min = extremes.min,
  3467. max = extremes.max,
  3468. minIndex,
  3469. maxIndex,
  3470. slope,
  3471. hasBreaks = axis.isXAxis && !!axis.options.breaks,
  3472. isOrdinal = axis.options.ordinal,
  3473. overscrollPointsRange = Number.MAX_VALUE,
  3474. ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries,
  3475. i,
  3476. hasBoostedSeries;
  3477. // Apply the ordinal logic
  3478. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  3479. axis.series.forEach(function (series, i) {
  3480. uniqueOrdinalPositions = [];
  3481. if ((!ignoreHiddenSeries || series.visible !== false) &&
  3482. (series.takeOrdinalPosition !== false || hasBreaks)) {
  3483. // concatenate the processed X data into the existing
  3484. // positions, or the empty array
  3485. ordinalPositions = ordinalPositions.concat(series.processedXData);
  3486. len = ordinalPositions.length;
  3487. // remove duplicates (#1588)
  3488. ordinalPositions.sort(function (a, b) {
  3489. // without a custom function it is sorted as strings
  3490. return a - b;
  3491. });
  3492. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  3493. // Check for a single-point series:
  3494. series.closestPointRange, overscrollPointsRange));
  3495. if (len) {
  3496. i = 0;
  3497. while (i < len - 1) {
  3498. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  3499. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  3500. }
  3501. i++;
  3502. }
  3503. // Check first item:
  3504. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  3505. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  3506. }
  3507. ordinalPositions = uniqueOrdinalPositions;
  3508. }
  3509. }
  3510. if (series.isSeriesBoosting) {
  3511. hasBoostedSeries = true;
  3512. }
  3513. });
  3514. if (hasBoostedSeries) {
  3515. ordinalPositions.length = 0;
  3516. }
  3517. // cache the length
  3518. len = ordinalPositions.length;
  3519. // Check if we really need the overhead of mapping axis data
  3520. // against the ordinal positions. If the series consist of
  3521. // evenly spaced data any way, we don't need any ordinal logic.
  3522. if (len > 2) { // two points have equal distance by default
  3523. dist = ordinalPositions[1] - ordinalPositions[0];
  3524. i = len - 1;
  3525. while (i-- && !useOrdinal) {
  3526. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  3527. useOrdinal = true;
  3528. }
  3529. }
  3530. // When zooming in on a week, prevent axis padding for
  3531. // weekends even though the data within the week is evenly
  3532. // spaced.
  3533. if (!axis.options.keepOrdinalPadding &&
  3534. (ordinalPositions[0] - min > dist ||
  3535. max - ordinalPositions[ordinalPositions.length - 1] >
  3536. dist)) {
  3537. useOrdinal = true;
  3538. }
  3539. }
  3540. else if (axis.options.overscroll) {
  3541. if (len === 2) {
  3542. // Exactly two points, distance for overscroll is fixed:
  3543. overscrollPointsRange =
  3544. ordinalPositions[1] - ordinalPositions[0];
  3545. }
  3546. else if (len === 1) {
  3547. // We have just one point, closest distance is unknown.
  3548. // Assume then it is last point and overscrolled range:
  3549. overscrollPointsRange = axis.options.overscroll;
  3550. ordinalPositions = [
  3551. ordinalPositions[0],
  3552. ordinalPositions[0] + overscrollPointsRange
  3553. ];
  3554. }
  3555. else {
  3556. // In case of zooming in on overscrolled range, stick to
  3557. // the old range:
  3558. overscrollPointsRange = ordinal.overscrollPointsRange;
  3559. }
  3560. }
  3561. // Record the slope and offset to compute the linear values from
  3562. // the array index. Since the ordinal positions may exceed the
  3563. // current range, get the start and end positions within it
  3564. // (#719, #665b)
  3565. if (useOrdinal || axis.forceOrdinal) {
  3566. if (axis.options.overscroll) {
  3567. ordinal.overscrollPointsRange = overscrollPointsRange;
  3568. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  3569. }
  3570. // Register
  3571. ordinal.positions = ordinalPositions;
  3572. // This relies on the ordinalPositions being set. Use
  3573. // Math.max and Math.min to prevent padding on either sides
  3574. // of the data.
  3575. minIndex = axis.ordinal2lin(// #5979
  3576. Math.max(min, ordinalPositions[0]), true);
  3577. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  3578. // Set the slope and offset of the values compared to the
  3579. // indices in the ordinal positions
  3580. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  3581. ordinal.offset = min - (minIndex * slope);
  3582. }
  3583. else {
  3584. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  3585. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  3586. void 0;
  3587. }
  3588. }
  3589. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  3590. ordinal.groupIntervalFactor = null; // reset for next run
  3591. };
  3592. /**
  3593. * Get the ordinal positions for the entire data set. This is necessary
  3594. * in chart panning because we need to find out what points or data
  3595. * groups are available outside the visible range. When a panning
  3596. * operation starts, if an index for the given grouping does not exists,
  3597. * it is created and cached. This index is deleted on updated data, so
  3598. * it will be regenerated the next time a panning operation starts.
  3599. *
  3600. * @private
  3601. */
  3602. Composition.prototype.getExtendedPositions = function () {
  3603. var ordinal = this,
  3604. axis = ordinal.axis,
  3605. axisProto = axis.constructor.prototype,
  3606. chart = axis.chart,
  3607. grouping = axis.series[0].currentDataGrouping,
  3608. ordinalIndex = ordinal.index,
  3609. key = grouping ?
  3610. grouping.count + grouping.unitName :
  3611. 'raw',
  3612. overscroll = axis.options.overscroll,
  3613. extremes = axis.getExtremes(),
  3614. fakeAxis,
  3615. fakeSeries;
  3616. // If this is the first time, or the ordinal index is deleted by
  3617. // updatedData,
  3618. // create it.
  3619. if (!ordinalIndex) {
  3620. ordinalIndex = ordinal.index = {};
  3621. }
  3622. if (!ordinalIndex[key]) {
  3623. // Create a fake axis object where the extended ordinal
  3624. // positions are emulated
  3625. fakeAxis = {
  3626. series: [],
  3627. chart: chart,
  3628. forceOrdinal: false,
  3629. getExtremes: function () {
  3630. return {
  3631. min: extremes.dataMin,
  3632. max: extremes.dataMax + overscroll
  3633. };
  3634. },
  3635. getGroupPixelWidth: axisProto.getGroupPixelWidth,
  3636. getTimeTicks: axisProto.getTimeTicks,
  3637. options: {
  3638. ordinal: true
  3639. },
  3640. ordinal: {
  3641. getGroupIntervalFactor: this.getGroupIntervalFactor
  3642. },
  3643. ordinal2lin: axisProto.ordinal2lin,
  3644. val2lin: axisProto.val2lin // #2590
  3645. };
  3646. fakeAxis.ordinal.axis = fakeAxis;
  3647. // Add the fake series to hold the full data, then apply
  3648. // processData to it
  3649. axis.series.forEach(function (series) {
  3650. fakeSeries = {
  3651. xAxis: fakeAxis,
  3652. xData: series.xData.slice(),
  3653. chart: chart,
  3654. destroyGroupedData: H.noop,
  3655. getProcessedData: Series.prototype.getProcessedData
  3656. };
  3657. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  3658. fakeSeries.options = {
  3659. dataGrouping: grouping ? {
  3660. enabled: true,
  3661. forced: true,
  3662. // doesn't matter which, use the fastest
  3663. approximation: 'open',
  3664. units: [[
  3665. grouping.unitName,
  3666. [grouping.count]
  3667. ]]
  3668. } : {
  3669. enabled: false
  3670. }
  3671. };
  3672. fakeAxis.series.push(fakeSeries);
  3673. series.processData.apply(fakeSeries);
  3674. // Force to use the ordinal when points are evenly spaced
  3675. // (e.g. weeks), #3825.
  3676. if (fakeSeries.closestPointRange !== fakeSeries.basePointRange && fakeSeries.currentDataGrouping) {
  3677. fakeAxis.forceOrdinal = true;
  3678. }
  3679. });
  3680. // Run beforeSetTickPositions to compute the ordinalPositions
  3681. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  3682. // Cache it
  3683. ordinalIndex[key] = fakeAxis.ordinal.positions;
  3684. }
  3685. return ordinalIndex[key];
  3686. };
  3687. /**
  3688. * Find the factor to estimate how wide the plot area would have been if
  3689. * ordinal gaps were included. This value is used to compute an imagined
  3690. * plot width in order to establish the data grouping interval.
  3691. *
  3692. * A real world case is the intraday-candlestick example. Without this
  3693. * logic, it would show the correct data grouping when viewing a range
  3694. * within each day, but once moving the range to include the gap between
  3695. * two days, the interval would include the cut-away night hours and the
  3696. * data grouping would be wrong. So the below method tries to compensate
  3697. * by identifying the most common point interval, in this case days.
  3698. *
  3699. * An opposite case is presented in issue #718. We have a long array of
  3700. * daily data, then one point is appended one hour after the last point.
  3701. * We expect the data grouping not to change.
  3702. *
  3703. * In the future, if we find cases where this estimation doesn't work
  3704. * optimally, we might need to add a second pass to the data grouping
  3705. * logic, where we do another run with a greater interval if the number
  3706. * of data groups is more than a certain fraction of the desired group
  3707. * count.
  3708. *
  3709. * @private
  3710. */
  3711. Composition.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
  3712. var ordinal = this,
  3713. axis = ordinal.axis,
  3714. i,
  3715. processedXData = series.processedXData,
  3716. len = processedXData.length,
  3717. distances = [],
  3718. median,
  3719. groupIntervalFactor = ordinal.groupIntervalFactor;
  3720. // Only do this computation for the first series, let the other
  3721. // inherit it (#2416)
  3722. if (!groupIntervalFactor) {
  3723. // Register all the distances in an array
  3724. for (i = 0; i < len - 1; i++) {
  3725. distances[i] =
  3726. processedXData[i + 1] - processedXData[i];
  3727. }
  3728. // Sort them and find the median
  3729. distances.sort(function (a, b) {
  3730. return a - b;
  3731. });
  3732. median = distances[Math.floor(len / 2)];
  3733. // Compensate for series that don't extend through the entire
  3734. // axis extent. #1675.
  3735. xMin = Math.max(xMin, processedXData[0]);
  3736. xMax = Math.min(xMax, processedXData[len - 1]);
  3737. ordinal.groupIntervalFactor = groupIntervalFactor =
  3738. (len * median) / (xMax - xMin);
  3739. }
  3740. // Return the factor needed for data grouping
  3741. return groupIntervalFactor;
  3742. };
  3743. /**
  3744. * Get ticks for an ordinal axis within a range where points don't
  3745. * exist. It is required when overscroll is enabled. We can't base on
  3746. * points, because we may not have any, so we use approximated
  3747. * pointRange and generate these ticks between Axis.dataMax,
  3748. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  3749. * navigator scrolling.
  3750. *
  3751. * @private
  3752. */
  3753. Composition.prototype.getOverscrollPositions = function () {
  3754. var ordinal = this,
  3755. axis = ordinal.axis,
  3756. extraRange = axis.options.overscroll,
  3757. distance = ordinal.overscrollPointsRange,
  3758. positions = [],
  3759. max = axis.dataMax;
  3760. if (defined(distance)) {
  3761. // Max + pointRange because we need to scroll to the last
  3762. positions.push(max);
  3763. while (max <= axis.dataMax + extraRange) {
  3764. max += distance;
  3765. positions.push(max);
  3766. }
  3767. }
  3768. return positions;
  3769. };
  3770. /**
  3771. * Make the tick intervals closer because the ordinal gaps make the
  3772. * ticks spread out or cluster.
  3773. *
  3774. * @private
  3775. */
  3776. Composition.prototype.postProcessTickInterval = function (tickInterval) {
  3777. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  3778. // This is a case where this algorithm doesn't work optimally. In
  3779. // this case, the tick labels are spread out per week, but all the
  3780. // gaps reside within weeks. So we have a situation where the labels
  3781. // are courser than the ordinal gaps, and thus the tick interval
  3782. // should not be altered.
  3783. var ordinal = this,
  3784. axis = ordinal.axis,
  3785. ordinalSlope = ordinal.slope,
  3786. ret;
  3787. if (ordinalSlope) {
  3788. if (!axis.options.breaks) {
  3789. ret = tickInterval / (ordinalSlope / axis.closestPointRange);
  3790. }
  3791. else {
  3792. ret = axis.closestPointRange || tickInterval; // #7275
  3793. }
  3794. }
  3795. else {
  3796. ret = tickInterval;
  3797. }
  3798. return ret;
  3799. };
  3800. return Composition;
  3801. }());
  3802. OrdinalAxis.Composition = Composition;
  3803. /* *
  3804. *
  3805. * Functions
  3806. *
  3807. * */
  3808. /**
  3809. * Extends the axis with ordinal support.
  3810. *
  3811. * @private
  3812. *
  3813. * @param AxisClass
  3814. * Axis class to extend.
  3815. *
  3816. * @param ChartClass
  3817. * Chart class to use.
  3818. *
  3819. * @param SeriesClass
  3820. * Series class to use.
  3821. */
  3822. function compose(AxisClass, ChartClass, SeriesClass) {
  3823. AxisClass.keepProps.push('ordinal');
  3824. var axisProto = AxisClass.prototype;
  3825. /**
  3826. * In an ordinal axis, there might be areas with dense consentrations of
  3827. * points, then large gaps between some. Creating equally distributed
  3828. * ticks over this entire range may lead to a huge number of ticks that
  3829. * will later be removed. So instead, break the positions up in
  3830. * segments, find the tick positions for each segment then concatenize
  3831. * them. This method is used from both data grouping logic and X axis
  3832. * tick position logic.
  3833. *
  3834. * @private
  3835. */
  3836. AxisClass.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  3837. if (positions === void 0) { positions = []; }
  3838. if (closestDistance === void 0) { closestDistance = 0; }
  3839. var start = 0,
  3840. end,
  3841. segmentPositions,
  3842. higherRanks = {},
  3843. hasCrossedHigherRank,
  3844. info,
  3845. posLength,
  3846. outsideMax,
  3847. groupPositions = [],
  3848. lastGroupPosition = -Number.MAX_VALUE,
  3849. tickPixelIntervalOption = this.options.tickPixelInterval,
  3850. time = this.chart.time,
  3851. // Record all the start positions of a segment, to use when
  3852. // deciding what's a gap in the data.
  3853. segmentStarts = [];
  3854. // The positions are not always defined, for example for ordinal
  3855. // positions when data has regular interval (#1557, #2090)
  3856. if ((!this.options.ordinal && !this.options.breaks) ||
  3857. !positions ||
  3858. positions.length < 3 ||
  3859. typeof min === 'undefined') {
  3860. return time.getTimeTicks.apply(time, arguments);
  3861. }
  3862. // Analyze the positions array to split it into segments on gaps
  3863. // larger than 5 times the closest distance. The closest distance is
  3864. // already found at this point, so we reuse that instead of
  3865. // computing it again.
  3866. posLength = positions.length;
  3867. for (end = 0; end < posLength; end++) {
  3868. outsideMax = end && positions[end - 1] > max;
  3869. if (positions[end] < min) { // Set the last position before min
  3870. start = end;
  3871. }
  3872. if (end === posLength - 1 ||
  3873. positions[end + 1] - positions[end] > closestDistance * 5 ||
  3874. outsideMax) {
  3875. // For each segment, calculate the tick positions from the
  3876. // getTimeTicks utility function. The interval will be the
  3877. // same regardless of how long the segment is.
  3878. if (positions[end] > lastGroupPosition) { // #1475
  3879. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  3880. // Prevent duplicate groups, for example for multiple
  3881. // segments within one larger time frame (#1475)
  3882. while (segmentPositions.length &&
  3883. segmentPositions[0] <= lastGroupPosition) {
  3884. segmentPositions.shift();
  3885. }
  3886. if (segmentPositions.length) {
  3887. lastGroupPosition =
  3888. segmentPositions[segmentPositions.length - 1];
  3889. }
  3890. segmentStarts.push(groupPositions.length);
  3891. groupPositions = groupPositions.concat(segmentPositions);
  3892. }
  3893. // Set start of next segment
  3894. start = end + 1;
  3895. }
  3896. if (outsideMax) {
  3897. break;
  3898. }
  3899. }
  3900. // Get the grouping info from the last of the segments. The info is
  3901. // the same for all segments.
  3902. if (segmentPositions) {
  3903. info = segmentPositions.info;
  3904. // Optionally identify ticks with higher rank, for example
  3905. // when the ticks have crossed midnight.
  3906. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  3907. end = groupPositions.length - 1;
  3908. // Compare points two by two
  3909. for (start = 1; start < end; start++) {
  3910. if (time.dateFormat('%d', groupPositions[start]) !==
  3911. time.dateFormat('%d', groupPositions[start - 1])) {
  3912. higherRanks[groupPositions[start]] = 'day';
  3913. hasCrossedHigherRank = true;
  3914. }
  3915. }
  3916. // If the complete array has crossed midnight, we want
  3917. // to mark the first positions also as higher rank
  3918. if (hasCrossedHigherRank) {
  3919. higherRanks[groupPositions[0]] = 'day';
  3920. }
  3921. info.higherRanks = higherRanks;
  3922. }
  3923. // Save the info
  3924. info.segmentStarts = segmentStarts;
  3925. groupPositions.info = info;
  3926. }
  3927. else {
  3928. error(12, false, this.chart);
  3929. }
  3930. // Don't show ticks within a gap in the ordinal axis, where the
  3931. // space between two points is greater than a portion of the tick
  3932. // pixel interval
  3933. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  3934. var length_1 = groupPositions.length,
  3935. i = length_1,
  3936. itemToRemove = void 0,
  3937. translated = void 0,
  3938. translatedArr = [],
  3939. lastTranslated = void 0,
  3940. medianDistance = void 0,
  3941. distance = void 0,
  3942. distances = [];
  3943. // Find median pixel distance in order to keep a reasonably even
  3944. // distance between ticks (#748)
  3945. while (i--) {
  3946. translated = this.translate(groupPositions[i]);
  3947. if (lastTranslated) {
  3948. distances[i] = lastTranslated - translated;
  3949. }
  3950. translatedArr[i] = lastTranslated = translated;
  3951. }
  3952. distances.sort();
  3953. medianDistance = distances[Math.floor(distances.length / 2)];
  3954. if (medianDistance < tickPixelIntervalOption * 0.6) {
  3955. medianDistance = null;
  3956. }
  3957. // Now loop over again and remove ticks where needed
  3958. i = groupPositions[length_1 - 1] > max ? length_1 - 1 : length_1; // #817
  3959. lastTranslated = void 0;
  3960. while (i--) {
  3961. translated = translatedArr[i];
  3962. distance = Math.abs(lastTranslated - translated);
  3963. // #4175 - when axis is reversed, the distance, is negative
  3964. // but tickPixelIntervalOption positive, so we need to
  3965. // compare the same values
  3966. // Remove ticks that are closer than 0.6 times the pixel
  3967. // interval from the one to the right, but not if it is
  3968. // close to the median distance (#748).
  3969. if (lastTranslated &&
  3970. distance < tickPixelIntervalOption * 0.8 &&
  3971. (medianDistance === null || distance < medianDistance * 0.8)) {
  3972. // Is this a higher ranked position with a normal
  3973. // position to the right?
  3974. if (higherRanks[groupPositions[i]] &&
  3975. !higherRanks[groupPositions[i + 1]]) {
  3976. // Yes: remove the lower ranked neighbour to the
  3977. // right
  3978. itemToRemove = i + 1;
  3979. lastTranslated = translated; // #709
  3980. }
  3981. else {
  3982. // No: remove this one
  3983. itemToRemove = i;
  3984. }
  3985. groupPositions.splice(itemToRemove, 1);
  3986. }
  3987. else {
  3988. lastTranslated = translated;
  3989. }
  3990. }
  3991. }
  3992. return groupPositions;
  3993. };
  3994. /**
  3995. * Translate from linear (internal) to axis value.
  3996. *
  3997. * @private
  3998. * @function Highcharts.Axis#lin2val
  3999. *
  4000. * @param {number} val
  4001. * The linear abstracted value.
  4002. *
  4003. * @param {boolean} [fromIndex]
  4004. * Translate from an index in the ordinal positions rather than a
  4005. * value.
  4006. *
  4007. * @return {number}
  4008. */
  4009. axisProto.lin2val = function (val, fromIndex) {
  4010. var axis = this,
  4011. ordinal = axis.ordinal,
  4012. ordinalPositions = ordinal.positions,
  4013. ret;
  4014. // the visible range contains only equally spaced values
  4015. if (!ordinalPositions) {
  4016. ret = val;
  4017. }
  4018. else {
  4019. var ordinalSlope = ordinal.slope,
  4020. ordinalOffset = ordinal.offset,
  4021. i = ordinalPositions.length - 1,
  4022. linearEquivalentLeft = void 0,
  4023. linearEquivalentRight = void 0,
  4024. distance = void 0;
  4025. // Handle the case where we translate from the index directly,
  4026. // used only when panning an ordinal axis
  4027. if (fromIndex) {
  4028. if (val < 0) { // out of range, in effect panning to the left
  4029. val = ordinalPositions[0];
  4030. }
  4031. else if (val > i) { // out of range, panning to the right
  4032. val = ordinalPositions[i];
  4033. }
  4034. else { // split it up
  4035. i = Math.floor(val);
  4036. distance = val - i; // the decimal
  4037. }
  4038. // Loop down along the ordinal positions. When the linear
  4039. // equivalent of i matches an ordinal position, interpolate
  4040. // between the left and right values.
  4041. }
  4042. else {
  4043. while (i--) {
  4044. linearEquivalentLeft =
  4045. (ordinalSlope * i) + ordinalOffset;
  4046. if (val >= linearEquivalentLeft) {
  4047. linearEquivalentRight =
  4048. (ordinalSlope *
  4049. (i + 1)) +
  4050. ordinalOffset;
  4051. // something between 0 and 1
  4052. distance = (val - linearEquivalentLeft) /
  4053. (linearEquivalentRight - linearEquivalentLeft);
  4054. break;
  4055. }
  4056. }
  4057. }
  4058. // If the index is within the range of the ordinal positions,
  4059. // return the associated or interpolated value. If not, just
  4060. // return the value.
  4061. return (typeof distance !== 'undefined' &&
  4062. typeof ordinalPositions[i] !== 'undefined' ?
  4063. ordinalPositions[i] + (distance ?
  4064. distance *
  4065. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  4066. 0) :
  4067. val);
  4068. }
  4069. return ret;
  4070. };
  4071. /**
  4072. * Translate from a linear axis value to the corresponding ordinal axis
  4073. * position. If there are no gaps in the ordinal axis this will be the
  4074. * same. The translated value is the value that the point would have if
  4075. * the axis were linear, using the same min and max.
  4076. *
  4077. * @private
  4078. * @function Highcharts.Axis#val2lin
  4079. *
  4080. * @param {number} val
  4081. * The axis value.
  4082. *
  4083. * @param {boolean} [toIndex]
  4084. * Whether to return the index in the ordinalPositions or the new value.
  4085. *
  4086. * @return {number}
  4087. */
  4088. axisProto.val2lin = function (val, toIndex) {
  4089. var axis = this,
  4090. ordinal = axis.ordinal,
  4091. ordinalPositions = ordinal.positions,
  4092. ret;
  4093. if (!ordinalPositions) {
  4094. ret = val;
  4095. }
  4096. else {
  4097. var ordinalLength = ordinalPositions.length,
  4098. i = void 0,
  4099. distance = void 0,
  4100. ordinalIndex = void 0;
  4101. // first look for an exact match in the ordinalpositions array
  4102. i = ordinalLength;
  4103. while (i--) {
  4104. if (ordinalPositions[i] === val) {
  4105. ordinalIndex = i;
  4106. break;
  4107. }
  4108. }
  4109. // if that failed, find the intermediate position between the
  4110. // two nearest values
  4111. i = ordinalLength - 1;
  4112. while (i--) {
  4113. if (val > ordinalPositions[i] || i === 0) { // interpolate
  4114. // something between 0 and 1
  4115. distance = (val - ordinalPositions[i]) /
  4116. (ordinalPositions[i + 1] - ordinalPositions[i]);
  4117. ordinalIndex = i + distance;
  4118. break;
  4119. }
  4120. }
  4121. ret = toIndex ?
  4122. ordinalIndex :
  4123. ordinal.slope *
  4124. (ordinalIndex || 0) +
  4125. ordinal.offset;
  4126. }
  4127. return ret;
  4128. };
  4129. // Record this to prevent overwriting by broken-axis module (#5979)
  4130. axisProto.ordinal2lin = axisProto.val2lin;
  4131. /* eslint-disable no-invalid-this */
  4132. addEvent(AxisClass, 'afterInit', function () {
  4133. var axis = this;
  4134. if (!axis.ordinal) {
  4135. axis.ordinal = new OrdinalAxis.Composition(axis);
  4136. }
  4137. });
  4138. addEvent(AxisClass, 'foundExtremes', function () {
  4139. var axis = this;
  4140. if (axis.isXAxis &&
  4141. defined(axis.options.overscroll) &&
  4142. axis.max === axis.dataMax &&
  4143. (
  4144. // Panning is an execption. We don't want to apply
  4145. // overscroll when panning over the dataMax
  4146. !axis.chart.mouseIsDown ||
  4147. axis.isInternal) && (
  4148. // Scrollbar buttons are the other execption:
  4149. !axis.eventArgs ||
  4150. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  4151. axis.max += axis.options.overscroll;
  4152. // Live data and buttons require translation for the min:
  4153. if (!axis.isInternal && defined(axis.userMin)) {
  4154. axis.min += axis.options.overscroll;
  4155. }
  4156. }
  4157. });
  4158. // For ordinal axis, that loads data async, redraw axis after data is
  4159. // loaded. If we don't do that, axis will have the same extremes as
  4160. // previously, but ordinal positions won't be calculated. See #10290
  4161. addEvent(AxisClass, 'afterSetScale', function () {
  4162. var axis = this;
  4163. if (axis.horiz && !axis.isDirty) {
  4164. axis.isDirty = axis.isOrdinal &&
  4165. axis.chart.navigator &&
  4166. !axis.chart.navigator.adaptToUpdatedData;
  4167. }
  4168. });
  4169. addEvent(AxisClass, 'initialAxisTranslation', function () {
  4170. var axis = this;
  4171. if (axis.ordinal) {
  4172. axis.ordinal.beforeSetTickPositions();
  4173. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  4174. }
  4175. });
  4176. // Extending the Chart.pan method for ordinal axes
  4177. addEvent(ChartClass, 'pan', function (e) {
  4178. var chart = this,
  4179. xAxis = chart.xAxis[0],
  4180. overscroll = xAxis.options.overscroll,
  4181. chartX = e.originalEvent.chartX,
  4182. panning = chart.options.chart.panning,
  4183. runBase = false;
  4184. if (panning &&
  4185. panning.type !== 'y' &&
  4186. xAxis.options.ordinal &&
  4187. xAxis.series.length) {
  4188. var mouseDownX = chart.mouseDownX,
  4189. extremes = xAxis.getExtremes(),
  4190. dataMax = extremes.dataMax,
  4191. min = extremes.min,
  4192. max = extremes.max,
  4193. trimmedRange = void 0,
  4194. hoverPoints = chart.hoverPoints,
  4195. closestPointRange = (xAxis.closestPointRange ||
  4196. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)),
  4197. pointPixelWidth = (xAxis.translationSlope *
  4198. (xAxis.ordinal.slope || closestPointRange)),
  4199. // how many ordinal units did we move?
  4200. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  4201. // get index of all the chart's points
  4202. extendedAxis = { ordinal: { positions: xAxis.ordinal.getExtendedPositions() } },
  4203. ordinalPositions = void 0,
  4204. searchAxisLeft = void 0,
  4205. lin2val = xAxis.lin2val,
  4206. val2lin = xAxis.val2lin,
  4207. searchAxisRight = void 0;
  4208. // we have an ordinal axis, but the data is equally spaced
  4209. if (!extendedAxis.ordinal.positions) {
  4210. runBase = true;
  4211. }
  4212. else if (Math.abs(movedUnits) > 1) {
  4213. // Remove active points for shared tooltip
  4214. if (hoverPoints) {
  4215. hoverPoints.forEach(function (point) {
  4216. point.setState();
  4217. });
  4218. }
  4219. if (movedUnits < 0) {
  4220. searchAxisLeft = extendedAxis;
  4221. searchAxisRight = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4222. }
  4223. else {
  4224. searchAxisLeft = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4225. searchAxisRight = extendedAxis;
  4226. }
  4227. // In grouped data series, the last ordinal position
  4228. // represents the grouped data, which is to the left of the
  4229. // real data max. If we don't compensate for this, we will
  4230. // be allowed to pan grouped data series passed the right of
  4231. // the plot area.
  4232. ordinalPositions = searchAxisRight.ordinal.positions;
  4233. if (dataMax >
  4234. ordinalPositions[ordinalPositions.length - 1]) {
  4235. ordinalPositions.push(dataMax);
  4236. }
  4237. // Get the new min and max values by getting the ordinal
  4238. // index for the current extreme, then add the moved units
  4239. // and translate back to values. This happens on the
  4240. // extended ordinal positions if the new position is out of
  4241. // range, else it happens on the current x axis which is
  4242. // smaller and faster.
  4243. chart.fixedRange = max - min;
  4244. trimmedRange = xAxis.navigatorAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  4245. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  4246. true // translate from index
  4247. ]), lin2val.apply(searchAxisRight, [
  4248. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  4249. true // translate from index
  4250. ]));
  4251. // Apply it if it is within the available data range
  4252. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  4253. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  4254. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  4255. }
  4256. chart.mouseDownX = chartX; // set new reference for next run
  4257. css(chart.container, { cursor: 'move' });
  4258. }
  4259. }
  4260. else {
  4261. runBase = true;
  4262. }
  4263. // revert to the linear chart.pan version
  4264. if (runBase || (panning && /y/.test(panning.type))) {
  4265. if (overscroll) {
  4266. xAxis.max = xAxis.dataMax + overscroll;
  4267. }
  4268. }
  4269. else {
  4270. e.preventDefault();
  4271. }
  4272. });
  4273. addEvent(SeriesClass, 'updatedData', function () {
  4274. var xAxis = this.xAxis;
  4275. // Destroy the extended ordinal index on updated data
  4276. if (xAxis && xAxis.options.ordinal) {
  4277. delete xAxis.ordinal.index;
  4278. }
  4279. });
  4280. /* eslint-enable no-invalid-this */
  4281. }
  4282. OrdinalAxis.compose = compose;
  4283. })(OrdinalAxis || (OrdinalAxis = {}));
  4284. OrdinalAxis.compose(Axis, Chart, Series); // @todo move to StockChart, remove from master
  4285. return OrdinalAxis;
  4286. });
  4287. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Series/Series.js'], _modules['Extensions/Stacking.js'], _modules['Core/Utilities.js']], function (Axis, Series, StackItem, U) {
  4288. /* *
  4289. *
  4290. * (c) 2009-2021 Torstein Honsi
  4291. *
  4292. * License: www.highcharts.com/license
  4293. *
  4294. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4295. *
  4296. * */
  4297. var addEvent = U.addEvent,
  4298. find = U.find,
  4299. fireEvent = U.fireEvent,
  4300. isArray = U.isArray,
  4301. isNumber = U.isNumber,
  4302. pick = U.pick;
  4303. /**
  4304. * Axis with support of broken data rows.
  4305. * @private
  4306. * @class
  4307. */
  4308. var BrokenAxis;
  4309. (function (BrokenAxis) {
  4310. /* *
  4311. *
  4312. * Functions
  4313. *
  4314. * */
  4315. /* eslint-disable valid-jsdoc */
  4316. /**
  4317. * Adds support for broken axes.
  4318. * @private
  4319. */
  4320. function compose(AxisClass, SeriesClass) {
  4321. if (AxisClass.keepProps.indexOf('brokenAxis') === -1) {
  4322. AxisClass.keepProps.push('brokenAxis');
  4323. var seriesProto = Series.prototype;
  4324. seriesProto.drawBreaks = seriesDrawBreaks;
  4325. seriesProto.gappedPath = seriesGappedPath;
  4326. addEvent(AxisClass, 'init', onInit);
  4327. addEvent(AxisClass, 'afterInit', onAfterInit);
  4328. addEvent(AxisClass, 'afterSetTickPositions', onAfterSetTickPositions);
  4329. addEvent(AxisClass, 'afterSetOptions', onAfterSetOptions);
  4330. addEvent(SeriesClass, 'afterGeneratePoints', onSeriesAfterGeneratePoints);
  4331. addEvent(SeriesClass, 'afterRender', onSeriesAfterRender);
  4332. }
  4333. return AxisClass;
  4334. }
  4335. BrokenAxis.compose = compose;
  4336. /**
  4337. * @private
  4338. */
  4339. function onAfterInit() {
  4340. if (typeof this.brokenAxis !== 'undefined') {
  4341. this.brokenAxis.setBreaks(this.options.breaks, false);
  4342. }
  4343. }
  4344. /**
  4345. * Force Axis to be not-ordinal when breaks are defined.
  4346. * @private
  4347. */
  4348. function onAfterSetOptions() {
  4349. var axis = this;
  4350. if (axis.brokenAxis && axis.brokenAxis.hasBreaks) {
  4351. axis.options.ordinal = false;
  4352. }
  4353. }
  4354. /**
  4355. * @private
  4356. */
  4357. function onAfterSetTickPositions() {
  4358. var axis = this,
  4359. brokenAxis = axis.brokenAxis;
  4360. if (brokenAxis &&
  4361. brokenAxis.hasBreaks) {
  4362. var tickPositions = axis.tickPositions,
  4363. info = axis.tickPositions.info,
  4364. newPositions = [];
  4365. for (var i = 0; i < tickPositions.length; i++) {
  4366. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  4367. newPositions.push(tickPositions[i]);
  4368. }
  4369. }
  4370. axis.tickPositions = newPositions;
  4371. axis.tickPositions.info = info;
  4372. }
  4373. }
  4374. /**
  4375. * @private
  4376. */
  4377. function onInit() {
  4378. var axis = this;
  4379. if (!axis.brokenAxis) {
  4380. axis.brokenAxis = new Additions(axis);
  4381. }
  4382. }
  4383. /**
  4384. * @private
  4385. */
  4386. function onSeriesAfterGeneratePoints() {
  4387. var _a = this,
  4388. isDirty = _a.isDirty,
  4389. connectNulls = _a.options.connectNulls,
  4390. points = _a.points,
  4391. xAxis = _a.xAxis,
  4392. yAxis = _a.yAxis;
  4393. // Set, or reset visibility of the points. Axis.setBreaks marks
  4394. // the series as isDirty
  4395. if (isDirty) {
  4396. var i = points.length;
  4397. while (i--) {
  4398. var point = points[i];
  4399. // Respect nulls inside the break (#4275)
  4400. var nullGap = point.y === null && connectNulls === false;
  4401. var isPointInBreak = (!nullGap && ((xAxis &&
  4402. xAxis.brokenAxis &&
  4403. xAxis.brokenAxis.isInAnyBreak(point.x,
  4404. true)) || (yAxis &&
  4405. yAxis.brokenAxis &&
  4406. yAxis.brokenAxis.isInAnyBreak(point.y,
  4407. true))));
  4408. // Set point.visible if in any break.
  4409. // If not in break, reset visible to original value.
  4410. point.visible = isPointInBreak ?
  4411. false :
  4412. point.options.visible !== false;
  4413. }
  4414. }
  4415. }
  4416. /**
  4417. * @private
  4418. */
  4419. function onSeriesAfterRender() {
  4420. this.drawBreaks(this.xAxis, ['x']);
  4421. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  4422. }
  4423. /**
  4424. * @private
  4425. */
  4426. function seriesDrawBreaks(axis, keys) {
  4427. var series = this,
  4428. points = series.points;
  4429. var breaks,
  4430. threshold,
  4431. eventName,
  4432. y;
  4433. if (axis && // #5950
  4434. axis.brokenAxis &&
  4435. axis.brokenAxis.hasBreaks) {
  4436. var brokenAxis_1 = axis.brokenAxis;
  4437. keys.forEach(function (key) {
  4438. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  4439. threshold = axis.isXAxis ?
  4440. axis.min :
  4441. pick(series.options.threshold, axis.min);
  4442. points.forEach(function (point) {
  4443. y = pick(point['stack' + key.toUpperCase()], point[key]);
  4444. breaks.forEach(function (brk) {
  4445. if (isNumber(threshold) && isNumber(y)) {
  4446. eventName = false;
  4447. if ((threshold < brk.from && y > brk.to) ||
  4448. (threshold > brk.from && y < brk.from)) {
  4449. eventName = 'pointBreak';
  4450. }
  4451. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  4452. (threshold > brk.from && y > brk.to && y < brk.from)) {
  4453. eventName = 'pointInBreak';
  4454. }
  4455. if (eventName) {
  4456. fireEvent(axis, eventName, { point: point, brk: brk });
  4457. }
  4458. }
  4459. });
  4460. });
  4461. });
  4462. }
  4463. }
  4464. /**
  4465. * Extend getGraphPath by identifying gaps in the data so that we
  4466. * can draw a gap in the line or area. This was moved from ordinal
  4467. * axis module to broken axis module as of #5045.
  4468. *
  4469. * @private
  4470. * @function Highcharts.Series#gappedPath
  4471. *
  4472. * @return {Highcharts.SVGPathArray}
  4473. * Gapped path
  4474. */
  4475. function seriesGappedPath() {
  4476. var currentDataGrouping = this.currentDataGrouping,
  4477. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  4478. points = this.points.slice(),
  4479. yAxis = this.yAxis;
  4480. var gapSize = this.options.gapSize,
  4481. i = points.length - 1,
  4482. stack;
  4483. /**
  4484. * Defines when to display a gap in the graph, together with the
  4485. * [gapUnit](plotOptions.series.gapUnit) option.
  4486. *
  4487. * In case when `dataGrouping` is enabled, points can be grouped
  4488. * into a larger time span. This can make the grouped points to
  4489. * have a greater distance than the absolute value of `gapSize`
  4490. * property, which will result in disappearing graph completely.
  4491. * To prevent this situation the mentioned distance between
  4492. * grouped points is used instead of previously defined
  4493. * `gapSize`.
  4494. *
  4495. * In practice, this option is most often used to visualize gaps
  4496. * in time series. In a stock chart, intraday data is available
  4497. * for daytime hours, while gaps will appear in nights and
  4498. * weekends.
  4499. *
  4500. * @see [gapUnit](plotOptions.series.gapUnit)
  4501. * @see [xAxis.breaks](#xAxis.breaks)
  4502. *
  4503. * @sample {highstock} stock/plotoptions/series-gapsize/
  4504. * Setting the gap size to 2 introduces gaps for weekends in
  4505. * daily datasets.
  4506. *
  4507. * @type {number}
  4508. * @default 0
  4509. * @product highstock
  4510. * @requires modules/broken-axis
  4511. * @apioption plotOptions.series.gapSize
  4512. */
  4513. /**
  4514. * Together with [gapSize](plotOptions.series.gapSize), this
  4515. * option defines where to draw gaps in the graph.
  4516. *
  4517. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  4518. * means that if the distance between two points is greater than
  4519. * 5 times that of the two closest points, the graph will be
  4520. * broken.
  4521. *
  4522. * When the `gapUnit` is `"value"`, the gap is based on absolute
  4523. * axis values, which on a datetime axis is milliseconds. This
  4524. * also applies to the navigator series that inherits gap
  4525. * options from the base series.
  4526. *
  4527. * @see [gapSize](plotOptions.series.gapSize)
  4528. *
  4529. * @type {string}
  4530. * @default relative
  4531. * @since 5.0.13
  4532. * @product highstock
  4533. * @validvalue ["relative", "value"]
  4534. * @requires modules/broken-axis
  4535. * @apioption plotOptions.series.gapUnit
  4536. */
  4537. if (gapSize && i > 0) { // #5008
  4538. // Gap unit is relative
  4539. if (this.options.gapUnit !== 'value') {
  4540. gapSize *= this.basePointRange;
  4541. }
  4542. // Setting a new gapSize in case dataGrouping is enabled
  4543. // (#7686)
  4544. if (groupingSize &&
  4545. groupingSize > gapSize &&
  4546. // Except when DG is forced (e.g. from other series)
  4547. // and has lower granularity than actual points (#11351)
  4548. groupingSize >= this.basePointRange) {
  4549. gapSize = groupingSize;
  4550. }
  4551. // extension for ordinal breaks
  4552. var current = void 0,
  4553. next = void 0;
  4554. while (i--) {
  4555. // Reassign next if it is not visible
  4556. if (!(next && next.visible !== false)) {
  4557. next = points[i + 1];
  4558. }
  4559. current = points[i];
  4560. // Skip iteration if one of the points is not visible
  4561. if (next.visible === false || current.visible === false) {
  4562. continue;
  4563. }
  4564. if (next.x - current.x > gapSize) {
  4565. var xRange = (current.x + next.x) / 2;
  4566. points.splice(// insert after this one
  4567. i + 1, 0, {
  4568. isNull: true,
  4569. x: xRange
  4570. });
  4571. // For stacked chart generate empty stack items,
  4572. // #6546
  4573. if (yAxis.stacking && this.options.stacking) {
  4574. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  4575. new StackItem(yAxis, yAxis.options
  4576. .stackLabels, false, xRange, this.stack);
  4577. stack.total = 0;
  4578. }
  4579. }
  4580. // Assign current to next for the upcoming iteration
  4581. next = current;
  4582. }
  4583. }
  4584. // Call base method
  4585. return this.getGraphPath(points);
  4586. }
  4587. /* *
  4588. *
  4589. * Class
  4590. *
  4591. * */
  4592. /**
  4593. * Provides support for broken axes.
  4594. * @private
  4595. * @class
  4596. */
  4597. var Additions = /** @class */ (function () {
  4598. /* *
  4599. *
  4600. * Constructors
  4601. *
  4602. * */
  4603. function Additions(axis) {
  4604. this.hasBreaks = false;
  4605. this.axis = axis;
  4606. }
  4607. /* *
  4608. *
  4609. * Static Functions
  4610. *
  4611. * */
  4612. /**
  4613. * @private
  4614. */
  4615. Additions.isInBreak = function (brk, val) {
  4616. var repeat = brk.repeat || Infinity,
  4617. from = brk.from,
  4618. length = brk.to - brk.from,
  4619. test = (val >= from ?
  4620. (val - from) % repeat :
  4621. repeat - ((from - val) % repeat));
  4622. var ret;
  4623. if (!brk.inclusive) {
  4624. ret = test < length && test !== 0;
  4625. }
  4626. else {
  4627. ret = test <= length;
  4628. }
  4629. return ret;
  4630. };
  4631. /**
  4632. * @private
  4633. */
  4634. Additions.lin2Val = function (val) {
  4635. var axis = this;
  4636. var brokenAxis = axis.brokenAxis;
  4637. var breakArray = brokenAxis && brokenAxis.breakArray;
  4638. if (!breakArray || !isNumber(val)) {
  4639. return val;
  4640. }
  4641. var nval = val,
  4642. brk,
  4643. i;
  4644. for (i = 0; i < breakArray.length; i++) {
  4645. brk = breakArray[i];
  4646. if (brk.from >= nval) {
  4647. break;
  4648. }
  4649. else if (brk.to < nval) {
  4650. nval += brk.len;
  4651. }
  4652. else if (Additions.isInBreak(brk, nval)) {
  4653. nval += brk.len;
  4654. }
  4655. }
  4656. return nval;
  4657. };
  4658. /**
  4659. * @private
  4660. */
  4661. Additions.val2Lin = function (val) {
  4662. var axis = this;
  4663. var brokenAxis = axis.brokenAxis;
  4664. var breakArray = brokenAxis && brokenAxis.breakArray;
  4665. if (!breakArray || !isNumber(val)) {
  4666. return val;
  4667. }
  4668. var nval = val,
  4669. brk,
  4670. i;
  4671. for (i = 0; i < breakArray.length; i++) {
  4672. brk = breakArray[i];
  4673. if (brk.to <= val) {
  4674. nval -= brk.len;
  4675. }
  4676. else if (brk.from >= val) {
  4677. break;
  4678. }
  4679. else if (Additions.isInBreak(brk, val)) {
  4680. nval -= (val - brk.from);
  4681. break;
  4682. }
  4683. }
  4684. return nval;
  4685. };
  4686. /* *
  4687. *
  4688. * Functions
  4689. *
  4690. * */
  4691. /**
  4692. * Returns the first break found where the x is larger then break.from
  4693. * and smaller then break.to.
  4694. *
  4695. * @param {number} x
  4696. * The number which should be within a break.
  4697. *
  4698. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  4699. * The array of breaks to search within.
  4700. *
  4701. * @return {Highcharts.XAxisBreaksOptions|undefined}
  4702. * Returns the first break found that matches, returns false if no break
  4703. * is found.
  4704. */
  4705. Additions.prototype.findBreakAt = function (x, breaks) {
  4706. return find(breaks, function (b) {
  4707. return b.from < x && x < b.to;
  4708. });
  4709. };
  4710. /**
  4711. * @private
  4712. */
  4713. Additions.prototype.isInAnyBreak = function (val, testKeep) {
  4714. var brokenAxis = this,
  4715. axis = brokenAxis.axis,
  4716. breaks = axis.options.breaks || [];
  4717. var i = breaks.length,
  4718. inbrk,
  4719. keep,
  4720. ret;
  4721. if (i && isNumber(val)) {
  4722. while (i--) {
  4723. if (Additions.isInBreak(breaks[i], val)) {
  4724. inbrk = true;
  4725. if (!keep) {
  4726. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  4727. }
  4728. }
  4729. }
  4730. if (inbrk && testKeep) {
  4731. ret = inbrk && !keep;
  4732. }
  4733. else {
  4734. ret = inbrk;
  4735. }
  4736. }
  4737. return ret;
  4738. };
  4739. /**
  4740. * Dynamically set or unset breaks in an axis. This function in lighter
  4741. * than usin Axis.update, and it also preserves animation.
  4742. *
  4743. * @private
  4744. * @function Highcharts.Axis#setBreaks
  4745. *
  4746. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  4747. * The breaks to add. When `undefined` it removes existing breaks.
  4748. *
  4749. * @param {boolean} [redraw=true]
  4750. * Whether to redraw the chart immediately.
  4751. */
  4752. Additions.prototype.setBreaks = function (breaks, redraw) {
  4753. var brokenAxis = this;
  4754. var axis = brokenAxis.axis;
  4755. var hasBreaks = (isArray(breaks) && !!breaks.length);
  4756. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  4757. brokenAxis.hasBreaks = hasBreaks;
  4758. axis.options.breaks = axis.userOptions.breaks = breaks;
  4759. axis.forceRedraw = true; // Force recalculation in setScale
  4760. // Recalculate series related to the axis.
  4761. axis.series.forEach(function (series) {
  4762. series.isDirty = true;
  4763. });
  4764. if (!hasBreaks && axis.val2lin === Additions.val2Lin) {
  4765. // Revert to prototype functions
  4766. delete axis.val2lin;
  4767. delete axis.lin2val;
  4768. }
  4769. if (hasBreaks) {
  4770. axis.userOptions.ordinal = false;
  4771. axis.lin2val = Additions.lin2Val;
  4772. axis.val2lin = Additions.val2Lin;
  4773. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  4774. // If trying to set extremes inside a break, extend min to
  4775. // after, and max to before the break ( #3857 )
  4776. if (brokenAxis.hasBreaks) {
  4777. var breaks_1 = (this.options.breaks || []);
  4778. var axisBreak = void 0;
  4779. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks_1))) {
  4780. newMin = axisBreak.to;
  4781. }
  4782. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks_1))) {
  4783. newMax = axisBreak.from;
  4784. }
  4785. // If both min and max is within the same break.
  4786. if (newMax < newMin) {
  4787. newMax = newMin;
  4788. }
  4789. }
  4790. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  4791. };
  4792. axis.setAxisTranslation = function () {
  4793. Axis.prototype.setAxisTranslation.call(this);
  4794. brokenAxis.unitLength = void 0;
  4795. if (brokenAxis.hasBreaks) {
  4796. var breaks_2 = axis.options.breaks || [],
  4797. // Temporary one:
  4798. breakArrayT_1 = [],
  4799. breakArray_1 = [],
  4800. pointRangePadding = pick(axis.pointRangePadding, 0);
  4801. var length_1 = 0,
  4802. inBrk_1,
  4803. repeat_1,
  4804. min_1 = axis.userMin || axis.min,
  4805. max_1 = axis.userMax || axis.max,
  4806. start_1,
  4807. i_1;
  4808. // Min & max check (#4247)
  4809. breaks_2.forEach(function (brk) {
  4810. repeat_1 = brk.repeat || Infinity;
  4811. if (isNumber(min_1) && isNumber(max_1)) {
  4812. if (Additions.isInBreak(brk, min_1)) {
  4813. min_1 += (brk.to % repeat_1) - (min_1 % repeat_1);
  4814. }
  4815. if (Additions.isInBreak(brk, max_1)) {
  4816. max_1 -= (max_1 % repeat_1) - (brk.from % repeat_1);
  4817. }
  4818. }
  4819. });
  4820. // Construct an array holding all breaks in the axis
  4821. breaks_2.forEach(function (brk) {
  4822. start_1 = brk.from;
  4823. repeat_1 = brk.repeat || Infinity;
  4824. if (isNumber(min_1) && isNumber(max_1)) {
  4825. while (start_1 - repeat_1 > min_1) {
  4826. start_1 -= repeat_1;
  4827. }
  4828. while (start_1 < min_1) {
  4829. start_1 += repeat_1;
  4830. }
  4831. for (i_1 = start_1; i_1 < max_1; i_1 += repeat_1) {
  4832. breakArrayT_1.push({
  4833. value: i_1,
  4834. move: 'in'
  4835. });
  4836. breakArrayT_1.push({
  4837. value: i_1 + brk.to - brk.from,
  4838. move: 'out',
  4839. size: brk.breakSize
  4840. });
  4841. }
  4842. }
  4843. });
  4844. breakArrayT_1.sort(function (a, b) {
  4845. return ((a.value === b.value) ?
  4846. ((a.move === 'in' ? 0 : 1) -
  4847. (b.move === 'in' ? 0 : 1)) :
  4848. a.value - b.value);
  4849. });
  4850. // Simplify the breaks
  4851. inBrk_1 = 0;
  4852. start_1 = min_1;
  4853. breakArrayT_1.forEach(function (brk) {
  4854. inBrk_1 += (brk.move === 'in' ? 1 : -1);
  4855. if (inBrk_1 === 1 && brk.move === 'in') {
  4856. start_1 = brk.value;
  4857. }
  4858. if (inBrk_1 === 0 && isNumber(start_1)) {
  4859. breakArray_1.push({
  4860. from: start_1,
  4861. to: brk.value,
  4862. len: brk.value - start_1 - (brk.size || 0)
  4863. });
  4864. length_1 += brk.value - start_1 - (brk.size || 0);
  4865. }
  4866. });
  4867. brokenAxis.breakArray = breakArray_1;
  4868. // Used with staticScale, and below the actual axis
  4869. // length, when breaks are substracted.
  4870. if (isNumber(min_1) && isNumber(max_1) && isNumber(axis.min)) {
  4871. brokenAxis.unitLength = max_1 - min_1 - length_1 +
  4872. pointRangePadding;
  4873. fireEvent(axis, 'afterBreaks');
  4874. if (axis.staticScale) {
  4875. axis.transA = axis.staticScale;
  4876. }
  4877. else if (brokenAxis.unitLength) {
  4878. axis.transA *=
  4879. (max_1 - axis.min + pointRangePadding) /
  4880. brokenAxis.unitLength;
  4881. }
  4882. if (pointRangePadding) {
  4883. axis.minPixelPadding =
  4884. axis.transA * (axis.minPointOffset || 0);
  4885. }
  4886. axis.min = min_1;
  4887. axis.max = max_1;
  4888. }
  4889. }
  4890. };
  4891. }
  4892. if (pick(redraw, true)) {
  4893. axis.chart.redraw();
  4894. }
  4895. };
  4896. return Additions;
  4897. }());
  4898. BrokenAxis.Additions = Additions;
  4899. })(BrokenAxis || (BrokenAxis = {}));
  4900. /* *
  4901. *
  4902. * Default Export
  4903. *
  4904. * */
  4905. return BrokenAxis;
  4906. });
  4907. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [_modules['Core/Globals.js'], _modules['Core/Axis/BrokenAxis.js']], function (Highcharts, BrokenAxis) {
  4908. var G = Highcharts;
  4909. // Compositions
  4910. BrokenAxis.compose(G.Axis, G.Series);
  4911. });
  4912. _registerModule(_modules, 'Extensions/DataGrouping.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/FormatUtilities.js'], _modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Tooltip.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Utilities.js']], function (Axis, DateTimeAxis, F, H, Point, Series, Tooltip, D, U) {
  4913. /* *
  4914. *
  4915. * (c) 2010-2021 Torstein Honsi
  4916. *
  4917. * License: www.highcharts.com/license
  4918. *
  4919. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4920. *
  4921. * */
  4922. var format = F.format;
  4923. var seriesProto = Series.prototype;
  4924. var addEvent = U.addEvent,
  4925. arrayMax = U.arrayMax,
  4926. arrayMin = U.arrayMin,
  4927. correctFloat = U.correctFloat,
  4928. defined = U.defined,
  4929. error = U.error,
  4930. extend = U.extend,
  4931. isNumber = U.isNumber,
  4932. merge = U.merge,
  4933. pick = U.pick;
  4934. /**
  4935. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  4936. */
  4937. /**
  4938. * The position of the point inside the group.
  4939. *
  4940. * @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
  4941. */
  4942. /**
  4943. * The position of the first or last point in the series inside the group.
  4944. *
  4945. * @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
  4946. */
  4947. /**
  4948. * @interface Highcharts.DataGroupingInfoObject
  4949. */ /**
  4950. * @name Highcharts.DataGroupingInfoObject#length
  4951. * @type {number}
  4952. */ /**
  4953. * @name Highcharts.DataGroupingInfoObject#options
  4954. * @type {Highcharts.SeriesOptionsType|undefined}
  4955. */ /**
  4956. * @name Highcharts.DataGroupingInfoObject#start
  4957. * @type {number}
  4958. */
  4959. ''; // detach doclets above
  4960. /* ************************************************************************** *
  4961. * Start data grouping module *
  4962. * ************************************************************************** */
  4963. /* eslint-disable no-invalid-this, valid-jsdoc */
  4964. /**
  4965. * Define the available approximation types. The data grouping
  4966. * approximations takes an array or numbers as the first parameter. In case
  4967. * of ohlc, four arrays are sent in as four parameters. Each array consists
  4968. * only of numbers. In case null values belong to the group, the property
  4969. * .hasNulls will be set to true on the array.
  4970. *
  4971. * @product highstock
  4972. *
  4973. * @private
  4974. * @name Highcharts.approximations
  4975. * @type {Highcharts.Dictionary<Function>}
  4976. */
  4977. var approximations = H.approximations = {
  4978. sum: function (arr) {
  4979. var len = arr.length,
  4980. ret;
  4981. // 1. it consists of nulls exclusive
  4982. if (!len && arr.hasNulls) {
  4983. ret = null;
  4984. // 2. it has a length and real values
  4985. }
  4986. else if (len) {
  4987. ret = 0;
  4988. while (len--) {
  4989. ret += arr[len];
  4990. }
  4991. }
  4992. // 3. it has zero length, so just return undefined
  4993. // => doNothing()
  4994. return ret;
  4995. },
  4996. average: function (arr) {
  4997. var len = arr.length,
  4998. ret = approximations.sum(arr);
  4999. // If we have a number, return it divided by the length. If not,
  5000. // return null or undefined based on what the sum method finds.
  5001. if (isNumber(ret) && len) {
  5002. ret = correctFloat(ret / len);
  5003. }
  5004. return ret;
  5005. },
  5006. // The same as average, but for series with multiple values, like area
  5007. // ranges.
  5008. averages: function () {
  5009. var ret = [];
  5010. [].forEach.call(arguments, function (arr) {
  5011. ret.push(approximations.average(arr));
  5012. });
  5013. // Return undefined when first elem. is undefined and let
  5014. // sum method handle null (#7377)
  5015. return typeof ret[0] === 'undefined' ? void 0 : ret;
  5016. },
  5017. open: function (arr) {
  5018. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  5019. },
  5020. high: function (arr) {
  5021. return arr.length ?
  5022. arrayMax(arr) :
  5023. (arr.hasNulls ? null : void 0);
  5024. },
  5025. low: function (arr) {
  5026. return arr.length ?
  5027. arrayMin(arr) :
  5028. (arr.hasNulls ? null : void 0);
  5029. },
  5030. close: function (arr) {
  5031. return arr.length ?
  5032. arr[arr.length - 1] :
  5033. (arr.hasNulls ? null : void 0);
  5034. },
  5035. // ohlc and range are special cases where a multidimensional array is
  5036. // input and an array is output
  5037. ohlc: function (open, high, low, close) {
  5038. open = approximations.open(open);
  5039. high = approximations.high(high);
  5040. low = approximations.low(low);
  5041. close = approximations.close(close);
  5042. if (isNumber(open) ||
  5043. isNumber(high) ||
  5044. isNumber(low) ||
  5045. isNumber(close)) {
  5046. return [open, high, low, close];
  5047. }
  5048. // else, return is undefined
  5049. },
  5050. range: function (low, high) {
  5051. low = approximations.low(low);
  5052. high = approximations.high(high);
  5053. if (isNumber(low) || isNumber(high)) {
  5054. return [low, high];
  5055. }
  5056. if (low === null && high === null) {
  5057. return null;
  5058. }
  5059. // else, return is undefined
  5060. }
  5061. };
  5062. var groupData = function (xData,
  5063. yData,
  5064. groupPositions,
  5065. approximation) {
  5066. var series = this,
  5067. data = series.data,
  5068. dataOptions = series.options && series.options.data,
  5069. groupedXData = [],
  5070. groupedYData = [],
  5071. groupMap = [],
  5072. dataLength = xData.length,
  5073. pointX,
  5074. pointY,
  5075. groupedY,
  5076. // when grouping the fake extended axis for panning,
  5077. // we don't need to consider y
  5078. handleYData = !!yData,
  5079. values = [],
  5080. approximationFn,
  5081. pointArrayMap = series.pointArrayMap,
  5082. pointArrayMapLength = pointArrayMap && pointArrayMap.length,
  5083. extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']),
  5084. groupAll = this.options.dataGrouping && this.options.dataGrouping.groupAll,
  5085. pos = 0,
  5086. start = 0,
  5087. valuesLen,
  5088. i,
  5089. j;
  5090. /**
  5091. * @private
  5092. */
  5093. function getApproximation(approx) {
  5094. if (typeof approx === 'function') {
  5095. return approx;
  5096. }
  5097. if (approximations[approx]) {
  5098. return approximations[approx];
  5099. }
  5100. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  5101. 'average'];
  5102. }
  5103. approximationFn = getApproximation(approximation);
  5104. // Calculate values array size from pointArrayMap length
  5105. if (pointArrayMapLength) {
  5106. pointArrayMap.forEach(function () {
  5107. values.push([]);
  5108. });
  5109. }
  5110. else {
  5111. values.push([]);
  5112. }
  5113. valuesLen = pointArrayMapLength || 1;
  5114. // Start with the first point within the X axis range (#2696)
  5115. for (i = 0; i <= dataLength; i++) {
  5116. if (xData[i] >= groupPositions[0]) {
  5117. break;
  5118. }
  5119. }
  5120. for (i; i <= dataLength; i++) {
  5121. // when a new group is entered, summarize and initialize
  5122. // the previous group
  5123. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  5124. xData[i] >= groupPositions[pos + 1]) ||
  5125. i === dataLength) { // get the last group
  5126. // get group x and y
  5127. pointX = groupPositions[pos];
  5128. series.dataGroupInfo = {
  5129. start: groupAll ? start : (series.cropStart + start),
  5130. length: values[0].length
  5131. };
  5132. groupedY = approximationFn.apply(series, values);
  5133. // By default, let options of the first grouped point be passed over
  5134. // to the grouped point. This allows preserving properties like
  5135. // `name` and `color` or custom properties. Implementers can
  5136. // override this from the approximation function, where they can
  5137. // write custom options to `this.dataGroupInfo.options`.
  5138. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  5139. // Convert numbers and arrays into objects
  5140. series.dataGroupInfo.options = merge(series.pointClass.prototype
  5141. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  5142. // Make sure the raw data (x, y, open, high etc) is not copied
  5143. // over and overwriting approximated data.
  5144. extendedPointArrayMap.forEach(function (key) {
  5145. delete series.dataGroupInfo.options[key];
  5146. });
  5147. }
  5148. // push the grouped data
  5149. if (typeof groupedY !== 'undefined') {
  5150. groupedXData.push(pointX);
  5151. groupedYData.push(groupedY);
  5152. groupMap.push(series.dataGroupInfo);
  5153. }
  5154. // reset the aggregate arrays
  5155. start = i;
  5156. for (j = 0; j < valuesLen; j++) {
  5157. values[j].length = 0; // faster than values[j] = []
  5158. values[j].hasNulls = false;
  5159. }
  5160. // Advance on the group positions
  5161. pos += 1;
  5162. // don't loop beyond the last group
  5163. if (i === dataLength) {
  5164. break;
  5165. }
  5166. }
  5167. // break out
  5168. if (i === dataLength) {
  5169. break;
  5170. }
  5171. // for each raw data point, push it to an array that contains all values
  5172. // for this specific group
  5173. if (pointArrayMap) {
  5174. var index = (series.options.dataGrouping &&
  5175. series.options.dataGrouping.groupAll ?
  5176. i : series.cropStart + i),
  5177. point = (data && data[index]) ||
  5178. series.pointClass.prototype.applyOptions.apply({
  5179. series: series
  5180. },
  5181. [dataOptions[index]]),
  5182. val = void 0;
  5183. for (j = 0; j < pointArrayMapLength; j++) {
  5184. val = point[pointArrayMap[j]];
  5185. if (isNumber(val)) {
  5186. values[j].push(val);
  5187. }
  5188. else if (val === null) {
  5189. values[j].hasNulls = true;
  5190. }
  5191. }
  5192. }
  5193. else {
  5194. pointY = handleYData ? yData[i] : null;
  5195. if (isNumber(pointY)) {
  5196. values[0].push(pointY);
  5197. }
  5198. else if (pointY === null) {
  5199. values[0].hasNulls = true;
  5200. }
  5201. }
  5202. }
  5203. return {
  5204. groupedXData: groupedXData,
  5205. groupedYData: groupedYData,
  5206. groupMap: groupMap
  5207. };
  5208. };
  5209. var anchorPoints = function (series,
  5210. groupedXData,
  5211. xMax) {
  5212. var options = series.options,
  5213. dataGroupingOptions = options.dataGrouping,
  5214. totalRange = series.currentDataGrouping && series.currentDataGrouping.gapSize;
  5215. var i;
  5216. // DataGrouping x-coordinates.
  5217. if (dataGroupingOptions && series.xData && totalRange && series.groupMap) {
  5218. var groupedDataLength = groupedXData.length - 1,
  5219. anchor = dataGroupingOptions.anchor,
  5220. firstAnchor = pick(dataGroupingOptions.firstAnchor,
  5221. anchor),
  5222. lastAnchor = pick(dataGroupingOptions.lastAnchor,
  5223. anchor);
  5224. // Anchor points that are not extremes.
  5225. if (anchor && anchor !== 'start') {
  5226. var shiftInterval = totalRange *
  5227. { middle: 0.5,
  5228. end: 1 }[anchor];
  5229. i = groupedXData.length - 1;
  5230. while (i-- && i > 0) {
  5231. groupedXData[i] += shiftInterval;
  5232. }
  5233. }
  5234. // Change the first point position, but only when it is
  5235. // the first point in the data set not in the current zoom.
  5236. if (firstAnchor &&
  5237. firstAnchor !== 'start' &&
  5238. series.xData[0] >= groupedXData[0]) {
  5239. var groupStart = series.groupMap[0].start,
  5240. groupLength = series.groupMap[0].length;
  5241. var firstGroupstEnd = void 0;
  5242. if (isNumber(groupStart) && isNumber(groupLength)) {
  5243. firstGroupstEnd = groupStart + (groupLength - 1);
  5244. }
  5245. groupedXData[0] = {
  5246. middle: groupedXData[0] + 0.5 * totalRange,
  5247. end: groupedXData[0] + totalRange,
  5248. firstPoint: series.xData[0],
  5249. lastPoint: firstGroupstEnd && series.xData[firstGroupstEnd]
  5250. }[firstAnchor];
  5251. }
  5252. // Change the last point position but only when it is
  5253. // the last point in the data set not in the current zoom.
  5254. if (lastAnchor &&
  5255. lastAnchor !== 'start' &&
  5256. totalRange &&
  5257. groupedXData[groupedDataLength] >= xMax - totalRange) {
  5258. var lastGroupStart = series.groupMap[series.groupMap.length - 1].start;
  5259. groupedXData[groupedDataLength] = {
  5260. middle: groupedXData[groupedDataLength] + 0.5 * totalRange,
  5261. end: groupedXData[groupedDataLength] + totalRange,
  5262. firstPoint: lastGroupStart && series.xData[lastGroupStart],
  5263. lastPoint: series.xData[series.xData.length - 1]
  5264. }[lastAnchor];
  5265. }
  5266. }
  5267. };
  5268. var adjustExtremes = function (xAxis,
  5269. groupedXData) {
  5270. // Make sure the X axis extends to show the first group (#2533)
  5271. // But only for visible series (#5493, #6393)
  5272. if (defined(groupedXData[0]) &&
  5273. isNumber(xAxis.min) &&
  5274. isNumber(xAxis.dataMin) &&
  5275. groupedXData[0] < xAxis.min) {
  5276. if ((!defined(xAxis.options.min) &&
  5277. xAxis.min <= xAxis.dataMin) ||
  5278. xAxis.min === xAxis.dataMin) {
  5279. xAxis.min = Math.min(groupedXData[0],
  5280. xAxis.min);
  5281. }
  5282. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  5283. }
  5284. // When the last anchor set, change the extremes that
  5285. // the last point is visible (#12455).
  5286. if (defined(groupedXData[groupedXData.length - 1]) &&
  5287. isNumber(xAxis.max) &&
  5288. isNumber(xAxis.dataMax) &&
  5289. groupedXData[groupedXData.length - 1] > xAxis.max) {
  5290. if ((!defined(xAxis.options.max) &&
  5291. isNumber(xAxis.dataMax) &&
  5292. xAxis.max >= xAxis.dataMax) || xAxis.max === xAxis.dataMax) {
  5293. xAxis.max = Math.max(groupedXData[groupedXData.length - 1], xAxis.max);
  5294. }
  5295. xAxis.dataMax = Math.max(groupedXData[groupedXData.length - 1], xAxis.dataMax);
  5296. }
  5297. };
  5298. var dataGrouping = {
  5299. approximations: approximations,
  5300. groupData: groupData
  5301. };
  5302. // -----------------------------------------------------------------------------
  5303. // The following code applies to implementation of data grouping on a Series
  5304. var baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  5305. /** @ignore */
  5306. commonOptions = {
  5307. // enabled: null, // (true for stock charts, false for basic),
  5308. // forced: undefined,
  5309. groupPixelWidth: 2,
  5310. // the first one is the point or start value, the second is the start
  5311. // value if we're dealing with range, the third one is the end value if
  5312. // dealing with a range
  5313. dateTimeLabelFormats: {
  5314. millisecond: [
  5315. '%A, %b %e, %H:%M:%S.%L',
  5316. '%A, %b %e, %H:%M:%S.%L',
  5317. '-%H:%M:%S.%L'
  5318. ],
  5319. second: [
  5320. '%A, %b %e, %H:%M:%S',
  5321. '%A, %b %e, %H:%M:%S',
  5322. '-%H:%M:%S'
  5323. ],
  5324. minute: [
  5325. '%A, %b %e, %H:%M',
  5326. '%A, %b %e, %H:%M',
  5327. '-%H:%M'
  5328. ],
  5329. hour: [
  5330. '%A, %b %e, %H:%M',
  5331. '%A, %b %e, %H:%M',
  5332. '-%H:%M'
  5333. ],
  5334. day: [
  5335. '%A, %b %e, %Y',
  5336. '%A, %b %e',
  5337. '-%A, %b %e, %Y'
  5338. ],
  5339. week: [
  5340. 'Week from %A, %b %e, %Y',
  5341. '%A, %b %e',
  5342. '-%A, %b %e, %Y'
  5343. ],
  5344. month: [
  5345. '%B %Y',
  5346. '%B',
  5347. '-%B %Y'
  5348. ],
  5349. year: [
  5350. '%Y',
  5351. '%Y',
  5352. '-%Y'
  5353. ]
  5354. }
  5355. // smoothed = false, // enable this for navigator series only
  5356. }, specificOptions = {
  5357. line: {},
  5358. spline: {},
  5359. area: {},
  5360. areaspline: {},
  5361. arearange: {},
  5362. column: {
  5363. groupPixelWidth: 10
  5364. },
  5365. columnrange: {
  5366. groupPixelWidth: 10
  5367. },
  5368. candlestick: {
  5369. groupPixelWidth: 10
  5370. },
  5371. ohlc: {
  5372. groupPixelWidth: 5
  5373. }
  5374. },
  5375. // units are defined in a separate array to allow complete overriding in
  5376. // case of a user option
  5377. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  5378. [
  5379. 'millisecond',
  5380. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5381. ], [
  5382. 'second',
  5383. [1, 2, 5, 10, 15, 30]
  5384. ], [
  5385. 'minute',
  5386. [1, 2, 5, 10, 15, 30]
  5387. ], [
  5388. 'hour',
  5389. [1, 2, 3, 4, 6, 8, 12]
  5390. ], [
  5391. 'day',
  5392. [1]
  5393. ], [
  5394. 'week',
  5395. [1]
  5396. ], [
  5397. 'month',
  5398. [1, 3, 6]
  5399. ], [
  5400. 'year',
  5401. null
  5402. ]
  5403. ];
  5404. // Set default approximations to the prototypes if present. Properties are
  5405. // inherited down. Can be overridden for individual series types.
  5406. seriesProto.getDGApproximation = function () {
  5407. if (this.is('arearange')) {
  5408. return 'range';
  5409. }
  5410. if (this.is('ohlc')) {
  5411. return 'ohlc';
  5412. }
  5413. if (this.is('column')) {
  5414. return 'sum';
  5415. }
  5416. return 'average';
  5417. };
  5418. /**
  5419. * Takes parallel arrays of x and y data and groups the data into intervals
  5420. * defined by groupPositions, a collection of starting x values for each group.
  5421. *
  5422. * @private
  5423. * @function Highcharts.Series#groupData
  5424. *
  5425. * @param {Array<number>} xData
  5426. *
  5427. * @param {Array<number>|Array<Array<number>>} yData
  5428. *
  5429. * @param {boolean} groupPositions
  5430. *
  5431. * @param {string|Function} approximation
  5432. *
  5433. * @return {void}
  5434. */
  5435. seriesProto.groupData = groupData;
  5436. // Extend the basic processData method, that crops the data to the current zoom
  5437. // range, with data grouping logic.
  5438. seriesProto.processData = function () {
  5439. var series = this,
  5440. chart = series.chart,
  5441. options = series.options,
  5442. dataGroupingOptions = options.dataGrouping,
  5443. groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  5444. pick(dataGroupingOptions.enabled,
  5445. chart.options.isStock),
  5446. visible = (series.visible || !chart.options.chart.ignoreHiddenSeries),
  5447. hasGroupedData,
  5448. skip,
  5449. lastDataGrouping = this.currentDataGrouping,
  5450. currentDataGrouping,
  5451. croppedData,
  5452. revertRequireSorting = false;
  5453. // Run base method
  5454. series.forceCrop = groupingEnabled; // #334
  5455. series.groupPixelWidth = null; // #2110
  5456. series.hasProcessed = true; // #2692
  5457. // Data needs to be sorted for dataGrouping
  5458. if (groupingEnabled && !series.requireSorting) {
  5459. series.requireSorting = revertRequireSorting = true;
  5460. }
  5461. // Skip if processData returns false or if grouping is disabled (in that
  5462. // order)
  5463. skip = (baseProcessData.apply(series, arguments) === false ||
  5464. !groupingEnabled);
  5465. // Revert original requireSorting value if changed
  5466. if (revertRequireSorting) {
  5467. series.requireSorting = false;
  5468. }
  5469. if (!skip) {
  5470. series.destroyGroupedData();
  5471. var i = void 0,
  5472. processedXData = dataGroupingOptions.groupAll ?
  5473. series.xData :
  5474. series.processedXData,
  5475. processedYData = dataGroupingOptions.groupAll ?
  5476. series.yData :
  5477. series.processedYData,
  5478. plotSizeX = chart.plotSizeX,
  5479. xAxis = series.xAxis,
  5480. ordinal = xAxis.options.ordinal,
  5481. groupPixelWidth = series.groupPixelWidth =
  5482. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  5483. // Execute grouping if the amount of points is greater than the limit
  5484. // defined in groupPixelWidth
  5485. if (groupPixelWidth &&
  5486. processedXData &&
  5487. processedXData.length) {
  5488. hasGroupedData = true;
  5489. // Force recreation of point instances in series.translate, #5699
  5490. series.isDirty = true;
  5491. series.points = null; // #6709
  5492. var extremes = xAxis.getExtremes(),
  5493. xMin = extremes.min,
  5494. xMax = extremes.max,
  5495. groupIntervalFactor = (ordinal &&
  5496. xAxis.ordinal &&
  5497. xAxis.ordinal.getGroupIntervalFactor(xMin,
  5498. xMax,
  5499. series)) || 1,
  5500. interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  5501. groupIntervalFactor,
  5502. groupPositions = xAxis.getTimeTicks(DateTimeAxis.AdditionsClass.prototype.normalizeTimeTickInterval(interval,
  5503. dataGroupingOptions.units ||
  5504. defaultDataGroupingUnits),
  5505. // Processed data may extend beyond axis (#4907)
  5506. Math.min(xMin,
  5507. processedXData[0]),
  5508. Math.max(xMax,
  5509. processedXData[processedXData.length - 1]),
  5510. xAxis.options.startOfWeek,
  5511. processedXData,
  5512. series.closestPointRange),
  5513. groupedData = seriesProto.groupData.apply(series,
  5514. [
  5515. processedXData,
  5516. processedYData,
  5517. groupPositions,
  5518. dataGroupingOptions.approximation
  5519. ]),
  5520. groupedXData = groupedData.groupedXData,
  5521. groupedYData = groupedData.groupedYData,
  5522. gapSize = 0;
  5523. // The smoothed option is deprecated, instead,
  5524. // there is a fallback to the new anchoring mechanism. #12455.
  5525. if (dataGroupingOptions && dataGroupingOptions.smoothed && groupedXData.length) {
  5526. dataGroupingOptions.firstAnchor = 'firstPoint';
  5527. dataGroupingOptions.anchor = 'middle';
  5528. dataGroupingOptions.lastAnchor = 'lastPoint';
  5529. error(32, false, chart, { 'dataGrouping.smoothed': 'use dataGrouping.anchor' });
  5530. }
  5531. anchorPoints(series, groupedXData, xMax);
  5532. // Record what data grouping values were used
  5533. for (i = 1; i < groupPositions.length; i++) {
  5534. // The grouped gapSize needs to be the largest distance between
  5535. // the group to capture varying group sizes like months or DST
  5536. // crossing (#10000). Also check that the gap is not at the
  5537. // start of a segment.
  5538. if (!groupPositions.info.segmentStarts ||
  5539. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  5540. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  5541. }
  5542. }
  5543. currentDataGrouping = groupPositions.info;
  5544. currentDataGrouping.gapSize = gapSize;
  5545. series.closestPointRange = groupPositions.info.totalRange;
  5546. series.groupMap = groupedData.groupMap;
  5547. if (visible) {
  5548. adjustExtremes(xAxis, groupedXData);
  5549. }
  5550. // We calculated all group positions but we should render
  5551. // only the ones within the visible range
  5552. if (dataGroupingOptions.groupAll) {
  5553. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  5554. );
  5555. groupedXData = croppedData.xData;
  5556. groupedYData = croppedData.yData;
  5557. series.cropStart = croppedData.start; // #15005
  5558. }
  5559. // Set series props
  5560. series.processedXData = groupedXData;
  5561. series.processedYData = groupedYData;
  5562. }
  5563. else {
  5564. series.groupMap = null;
  5565. }
  5566. series.hasGroupedData = hasGroupedData;
  5567. series.currentDataGrouping = currentDataGrouping;
  5568. series.preventGraphAnimation =
  5569. (lastDataGrouping && lastDataGrouping.totalRange) !==
  5570. (currentDataGrouping && currentDataGrouping.totalRange);
  5571. }
  5572. };
  5573. // Destroy the grouped data points. #622, #740
  5574. seriesProto.destroyGroupedData = function () {
  5575. // Clear previous groups
  5576. if (this.groupedData) {
  5577. this.groupedData.forEach(function (point, i) {
  5578. if (point) {
  5579. this.groupedData[i] = point.destroy ?
  5580. point.destroy() : null;
  5581. }
  5582. }, this);
  5583. // Clears all:
  5584. // - `this.groupedData`
  5585. // - `this.points`
  5586. // - `preserve` object in series.update()
  5587. this.groupedData.length = 0;
  5588. }
  5589. };
  5590. // Override the generatePoints method by adding a reference to grouped data
  5591. seriesProto.generatePoints = function () {
  5592. baseGeneratePoints.apply(this);
  5593. // Record grouped data in order to let it be destroyed the next time
  5594. // processData runs
  5595. this.destroyGroupedData(); // #622
  5596. this.groupedData = this.hasGroupedData ? this.points : null;
  5597. };
  5598. // Override point prototype to throw a warning when trying to update grouped
  5599. // points.
  5600. addEvent(Point, 'update', function () {
  5601. if (this.dataGroup) {
  5602. error(24, false, this.series.chart);
  5603. return false;
  5604. }
  5605. });
  5606. // Extend the original method, make the tooltip's header reflect the grouped
  5607. // range.
  5608. addEvent(Tooltip, 'headerFormatter', function (e) {
  5609. var tooltip = this,
  5610. chart = this.chart,
  5611. time = chart.time,
  5612. labelConfig = e.labelConfig,
  5613. series = labelConfig.series,
  5614. options = series.options,
  5615. tooltipOptions = series.tooltipOptions,
  5616. dataGroupingOptions = options.dataGrouping,
  5617. xDateFormat = tooltipOptions.xDateFormat,
  5618. xDateFormatEnd,
  5619. xAxis = series.xAxis,
  5620. currentDataGrouping,
  5621. dateTimeLabelFormats,
  5622. labelFormats,
  5623. formattedKey,
  5624. formatString = tooltipOptions[(e.isFooter ? 'footer' : 'header') + 'Format'];
  5625. // apply only to grouped series
  5626. if (xAxis &&
  5627. xAxis.options.type === 'datetime' &&
  5628. dataGroupingOptions &&
  5629. isNumber(labelConfig.key)) {
  5630. // set variables
  5631. currentDataGrouping = series.currentDataGrouping;
  5632. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  5633. // Fallback to commonOptions (#9693)
  5634. commonOptions.dateTimeLabelFormats;
  5635. // if we have grouped data, use the grouping information to get the
  5636. // right format
  5637. if (currentDataGrouping) {
  5638. labelFormats =
  5639. dateTimeLabelFormats[currentDataGrouping.unitName];
  5640. if (currentDataGrouping.count === 1) {
  5641. xDateFormat = labelFormats[0];
  5642. }
  5643. else {
  5644. xDateFormat = labelFormats[1];
  5645. xDateFormatEnd = labelFormats[2];
  5646. }
  5647. // if not grouped, and we don't have set the xDateFormat option, get the
  5648. // best fit, so if the least distance between points is one minute, show
  5649. // it, but if the least distance is one day, skip hours and minutes etc.
  5650. }
  5651. else if (!xDateFormat && dateTimeLabelFormats) {
  5652. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  5653. }
  5654. // now format the key
  5655. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  5656. if (xDateFormatEnd) {
  5657. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  5658. }
  5659. // Replace default header style with class name
  5660. if (series.chart.styledMode) {
  5661. formatString = this.styledModeFormat(formatString);
  5662. }
  5663. // return the replaced format
  5664. e.text = format(formatString, {
  5665. point: extend(labelConfig.point, { key: formattedKey }),
  5666. series: series
  5667. }, chart);
  5668. e.preventDefault();
  5669. }
  5670. });
  5671. // Destroy grouped data on series destroy
  5672. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  5673. // Handle default options for data grouping. This must be set at runtime because
  5674. // some series types are defined after this.
  5675. addEvent(Series, 'afterSetOptions', function (e) {
  5676. var options = e.options,
  5677. type = this.type,
  5678. plotOptions = this.chart.options.plotOptions,
  5679. defaultOptions = D.defaultOptions.plotOptions[type].dataGrouping,
  5680. // External series, for example technical indicators should also
  5681. // inherit commonOptions which are not available outside this module
  5682. baseOptions = this.useCommonDataGrouping && commonOptions;
  5683. if (specificOptions[type] || baseOptions) { // #1284
  5684. if (!defaultOptions) {
  5685. defaultOptions = merge(commonOptions, specificOptions[type]);
  5686. }
  5687. var rangeSelector = this.chart.rangeSelector;
  5688. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  5689. // Set by the StockChart constructor:
  5690. plotOptions[type].dataGrouping, this.userOptions.dataGrouping, !options.isInternal &&
  5691. rangeSelector &&
  5692. isNumber(rangeSelector.selected) &&
  5693. rangeSelector.buttonOptions[rangeSelector.selected].dataGrouping);
  5694. }
  5695. });
  5696. // When resetting the scale reset the hasProccessed flag to avoid taking
  5697. // previous data grouping of neighbour series into accound when determining
  5698. // group pixel width (#2692).
  5699. addEvent(Axis, 'afterSetScale', function () {
  5700. this.series.forEach(function (series) {
  5701. series.hasProcessed = false;
  5702. });
  5703. });
  5704. // Get the data grouping pixel width based on the greatest defined individual
  5705. // width of the axis' series, and if whether one of the axes need grouping.
  5706. Axis.prototype.getGroupPixelWidth = function () {
  5707. var series = this.series,
  5708. len = series.length,
  5709. i,
  5710. groupPixelWidth = 0,
  5711. doGrouping = false,
  5712. dataLength,
  5713. dgOptions;
  5714. // If multiple series are compared on the same x axis, give them the same
  5715. // group pixel width (#334)
  5716. i = len;
  5717. while (i--) {
  5718. dgOptions = series[i].options.dataGrouping;
  5719. if (dgOptions) {
  5720. groupPixelWidth = Math.max(groupPixelWidth,
  5721. // Fallback to commonOptions (#9693)
  5722. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  5723. }
  5724. }
  5725. // If one of the series needs grouping, apply it to all (#1634)
  5726. i = len;
  5727. while (i--) {
  5728. dgOptions = series[i].options.dataGrouping;
  5729. if (dgOptions && series[i].hasProcessed) { // #2692
  5730. dataLength = (series[i].processedXData || series[i].data).length;
  5731. // Execute grouping if the amount of points is greater than the
  5732. // limit defined in groupPixelWidth
  5733. if (series[i].groupPixelWidth ||
  5734. dataLength >
  5735. (this.chart.plotSizeX / groupPixelWidth) ||
  5736. (dataLength && dgOptions.forced)) {
  5737. doGrouping = true;
  5738. }
  5739. }
  5740. }
  5741. return doGrouping ? groupPixelWidth : 0;
  5742. };
  5743. /**
  5744. * Highcharts Stock only. Force data grouping on all the axis' series.
  5745. *
  5746. * @product highstock
  5747. *
  5748. * @function Highcharts.Axis#setDataGrouping
  5749. *
  5750. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  5751. * A `dataGrouping` configuration. Use `false` to disable data grouping
  5752. * dynamically.
  5753. *
  5754. * @param {boolean} [redraw=true]
  5755. * Whether to redraw the chart or wait for a later call to
  5756. * {@link Chart#redraw}.
  5757. */
  5758. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  5759. var axis = this;
  5760. var i;
  5761. redraw = pick(redraw, true);
  5762. if (!dataGrouping) {
  5763. dataGrouping = {
  5764. forced: false,
  5765. units: null
  5766. };
  5767. }
  5768. // Axis is instantiated, update all series
  5769. if (this instanceof Axis) {
  5770. i = this.series.length;
  5771. while (i--) {
  5772. this.series[i].update({
  5773. dataGrouping: dataGrouping
  5774. }, false);
  5775. }
  5776. // Axis not yet instanciated, alter series options
  5777. }
  5778. else {
  5779. this.chart.options.series.forEach(function (seriesOptions) {
  5780. seriesOptions.dataGrouping = dataGrouping;
  5781. }, false);
  5782. }
  5783. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  5784. if (axis.ordinal) {
  5785. axis.ordinal.slope = void 0;
  5786. }
  5787. if (redraw) {
  5788. this.chart.redraw();
  5789. }
  5790. };
  5791. H.dataGrouping = dataGrouping;
  5792. /* eslint-enable no-invalid-this, valid-jsdoc */
  5793. /**
  5794. * Data grouping is the concept of sampling the data values into larger
  5795. * blocks in order to ease readability and increase performance of the
  5796. * JavaScript charts. Highcharts Stock by default applies data grouping when
  5797. * the points become closer than a certain pixel value, determined by
  5798. * the `groupPixelWidth` option.
  5799. *
  5800. * If data grouping is applied, the grouping information of grouped
  5801. * points can be read from the [Point.dataGroup](
  5802. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  5803. * the data itself are set, for example `name` or `color` or custom properties,
  5804. * the grouping logic doesn't know how to group it. In this case the options of
  5805. * the first point instance are copied over to the group point. This can be
  5806. * altered through a custom `approximation` callback function.
  5807. *
  5808. * @declare Highcharts.DataGroupingOptionsObject
  5809. * @product highstock
  5810. * @requires product:highstock
  5811. * @requires module:modules/datagrouping
  5812. * @apioption plotOptions.series.dataGrouping
  5813. */
  5814. /**
  5815. * Specifies how the points should be located on the X axis inside the group.
  5816. * Points that are extremes can be set separately. Available options:
  5817. *
  5818. * - `start` places the point at the beginning of the group
  5819. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5820. *
  5821. * - `middle` places the point in the middle of the group
  5822. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5823. *
  5824. * - `end` places the point at the end of the group
  5825. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5826. *
  5827. * @sample {highstock} stock/plotoptions/series-datagrouping-anchor
  5828. * Changing the point x-coordinate inside the group.
  5829. *
  5830. * @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
  5831. * @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
  5832. *
  5833. * @type {Highcharts.DataGroupingAnchor}
  5834. * @since 9.1.0
  5835. * @default start
  5836. * @apioption plotOptions.series.dataGrouping.anchor
  5837. */
  5838. /**
  5839. * The method of approximation inside a group. When for example 30 days
  5840. * are grouped into one month, this determines what value should represent
  5841. * the group. Possible values are "average", "averages", "open", "high",
  5842. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  5843. * is "ohlc" by default, which finds the open, high, low and close values
  5844. * within all the grouped data. For ranges, the approximation is "range",
  5845. * which finds the low and high values. For multi-dimensional data,
  5846. * like ranges and OHLC, "averages" will compute the average for each
  5847. * dimension.
  5848. *
  5849. * Custom aggregate methods can be added by assigning a callback function
  5850. * as the approximation. This function takes a numeric array as the
  5851. * argument and should return a single numeric value or `null`. Note
  5852. * that the numeric array will never contain null values, only true
  5853. * numbers. Instead, if null values are present in the raw data, the
  5854. * numeric array will have an `.hasNulls` property set to `true`. For
  5855. * single-value data sets the data is available in the first argument
  5856. * of the callback function. For OHLC data sets, all the open values
  5857. * are in the first argument, all high values in the second etc.
  5858. *
  5859. * Since v4.2.7, grouping meta data is available in the approximation
  5860. * callback from `this.dataGroupInfo`. It can be used to extract information
  5861. * from the raw data.
  5862. *
  5863. * Defaults to `average` for line-type series, `sum` for columns, `range`
  5864. * for range series and `ohlc` for OHLC and candlestick.
  5865. *
  5866. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  5867. * Approximation callback with custom data
  5868. * @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
  5869. * Simple approximation demo
  5870. *
  5871. * @type {Highcharts.DataGroupingApproximationValue|Function}
  5872. * @apioption plotOptions.series.dataGrouping.approximation
  5873. */
  5874. /**
  5875. * Datetime formats for the header of the tooltip in a stock chart.
  5876. * The format can vary within a chart depending on the currently selected
  5877. * time range and the current data grouping.
  5878. *
  5879. * The default formats are:
  5880. * ```js
  5881. * {
  5882. * millisecond: [
  5883. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  5884. * ],
  5885. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  5886. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5887. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5888. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5889. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5890. * month: ['%B %Y', '%B', '-%B %Y'],
  5891. * year: ['%Y', '%Y', '-%Y']
  5892. * }
  5893. * ```
  5894. *
  5895. * For each of these array definitions, the first item is the format
  5896. * used when the active time span is one unit. For instance, if the
  5897. * current data applies to one week, the first item of the week array
  5898. * is used. The second and third items are used when the active time
  5899. * span is more than two units. For instance, if the current data applies
  5900. * to two weeks, the second and third item of the week array are used,
  5901. * and applied to the start and end date of the time span.
  5902. *
  5903. * @type {object}
  5904. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  5905. */
  5906. /**
  5907. * Enable or disable data grouping.
  5908. *
  5909. * @type {boolean}
  5910. * @default true
  5911. * @apioption plotOptions.series.dataGrouping.enabled
  5912. */
  5913. /**
  5914. * Specifies how the first grouped point is positioned on the xAxis.
  5915. * If firstAnchor and/or lastAnchor are defined, then those options take
  5916. * precedence over anchor for the first and/or last grouped points.
  5917. * Available options:
  5918. *
  5919. * -`start` places the point at the beginning of the group
  5920. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5921. *
  5922. * -`middle` places the point in the middle of the group
  5923. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5924. *
  5925. * -`end` places the point at the end of the group
  5926. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5927. *
  5928. * -`firstPoint` the first point in the group
  5929. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  5930. *
  5931. * -`lastPoint` the last point in the group
  5932. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  5933. *
  5934. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  5935. * Applying first and last anchor.
  5936. *
  5937. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  5938. *
  5939. * @type {Highcharts.DataGroupingAnchorExtremes}
  5940. * @since 9.1.0
  5941. * @default start
  5942. * @apioption plotOptions.series.dataGrouping.firstAnchor
  5943. */
  5944. /**
  5945. * When data grouping is forced, it runs no matter how small the intervals
  5946. * are. This can be handy for example when the sum should be calculated
  5947. * for values appearing at random times within each hour.
  5948. *
  5949. * @type {boolean}
  5950. * @default false
  5951. * @apioption plotOptions.series.dataGrouping.forced
  5952. */
  5953. /**
  5954. * The approximate pixel width of each group. If for example a series
  5955. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5956. * is performed. If however the series contains so many points that
  5957. * the spacing is less than the groupPixelWidth, Highcharts will try
  5958. * to group it into appropriate groups so that each is more or less
  5959. * two pixels wide. If multiple series with different group pixel widths
  5960. * are drawn on the same x axis, all series will take the greatest width.
  5961. * For example, line series have 2px default group width, while column
  5962. * series have 10px. If combined, both the line and the column will
  5963. * have 10px by default.
  5964. *
  5965. * @type {number}
  5966. * @default 2
  5967. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  5968. */
  5969. /**
  5970. * By default only points within the visible range are grouped. Enabling this
  5971. * option will force data grouping to calculate all grouped points for a given
  5972. * dataset. That option prevents for example a column series from calculating
  5973. * a grouped point partially. The effect is similar to
  5974. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  5975. * not affect yAxis extremes.
  5976. *
  5977. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  5978. * Two series with the same data but different groupAll setting
  5979. *
  5980. * @type {boolean}
  5981. * @default false
  5982. * @since 6.1.0
  5983. * @apioption plotOptions.series.dataGrouping.groupAll
  5984. */
  5985. /**
  5986. * Specifies how the last grouped point is positioned on the xAxis.
  5987. * If firstAnchor and/or lastAnchor are defined, then those options take
  5988. * precedence over anchor for the first and/or last grouped points.
  5989. * Available options:
  5990. *
  5991. * -`start` places the point at the beginning of the group
  5992. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5993. *
  5994. * -`middle` places the point in the middle of the group
  5995. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5996. *
  5997. * -`end` places the point at the end of the group
  5998. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5999. *
  6000. * -`firstPoint` the first point in the group
  6001. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  6002. *
  6003. * -`lastPoint` the last point in the group
  6004. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  6005. *
  6006. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  6007. * Applying first and last anchor.
  6008. *
  6009. * @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
  6010. * Applying the last anchor in the chart with live data.
  6011. *
  6012. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  6013. *
  6014. * @type {Highcharts.DataGroupingAnchorExtremes}
  6015. * @since 9.1.0
  6016. * @default start
  6017. * @apioption plotOptions.series.dataGrouping.lastAnchor
  6018. */
  6019. /**
  6020. * Normally, a group is indexed by the start of that group, so for example
  6021. * when 30 daily values are grouped into one month, that month's x value
  6022. * will be the 1st of the month. This apparently shifts the data to
  6023. * the left. When the smoothed option is true, this is compensated for.
  6024. * The data is shifted to the middle of the group, and min and max
  6025. * values are preserved. Internally, this is used in the Navigator series.
  6026. *
  6027. * @type {boolean}
  6028. * @default false
  6029. * @deprecated
  6030. * @apioption plotOptions.series.dataGrouping.smoothed
  6031. */
  6032. /**
  6033. * An array determining what time intervals the data is allowed to be
  6034. * grouped to. Each array item is an array where the first value is
  6035. * the time unit and the second value another array of allowed multiples.
  6036. *
  6037. * Defaults to:
  6038. * ```js
  6039. * units: [[
  6040. * 'millisecond', // unit name
  6041. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  6042. * ], [
  6043. * 'second',
  6044. * [1, 2, 5, 10, 15, 30]
  6045. * ], [
  6046. * 'minute',
  6047. * [1, 2, 5, 10, 15, 30]
  6048. * ], [
  6049. * 'hour',
  6050. * [1, 2, 3, 4, 6, 8, 12]
  6051. * ], [
  6052. * 'day',
  6053. * [1]
  6054. * ], [
  6055. * 'week',
  6056. * [1]
  6057. * ], [
  6058. * 'month',
  6059. * [1, 3, 6]
  6060. * ], [
  6061. * 'year',
  6062. * null
  6063. * ]]
  6064. * ```
  6065. *
  6066. * @type {Array<Array<string,(Array<number>|null)>>}
  6067. * @apioption plotOptions.series.dataGrouping.units
  6068. */
  6069. /**
  6070. * The approximate pixel width of each group. If for example a series
  6071. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  6072. * is performed. If however the series contains so many points that
  6073. * the spacing is less than the groupPixelWidth, Highcharts will try
  6074. * to group it into appropriate groups so that each is more or less
  6075. * two pixels wide. Defaults to `10`.
  6076. *
  6077. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  6078. * Two series with the same data density but different groupPixelWidth
  6079. *
  6080. * @type {number}
  6081. * @default 10
  6082. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  6083. */
  6084. ''; // required by JSDoc parsing
  6085. return dataGrouping;
  6086. });
  6087. _registerModule(_modules, 'Series/OHLC/OHLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  6088. /* *
  6089. *
  6090. * (c) 2010-2021 Torstein Honsi
  6091. *
  6092. * License: www.highcharts.com/license
  6093. *
  6094. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6095. *
  6096. * */
  6097. var __extends = (this && this.__extends) || (function () {
  6098. var extendStatics = function (d,
  6099. b) {
  6100. extendStatics = Object.setPrototypeOf ||
  6101. ({ __proto__: [] } instanceof Array && function (d,
  6102. b) { d.__proto__ = b; }) ||
  6103. function (d,
  6104. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6105. return extendStatics(d, b);
  6106. };
  6107. return function (d, b) {
  6108. extendStatics(d, b);
  6109. function __() { this.constructor = d; }
  6110. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6111. };
  6112. })();
  6113. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6114. /* *
  6115. *
  6116. * Class
  6117. *
  6118. * */
  6119. var OHLCPoint = /** @class */ (function (_super) {
  6120. __extends(OHLCPoint, _super);
  6121. function OHLCPoint() {
  6122. /* *
  6123. *
  6124. * Properties
  6125. *
  6126. * */
  6127. var _this = _super !== null && _super.apply(this,
  6128. arguments) || this;
  6129. _this.close = void 0;
  6130. _this.high = void 0;
  6131. _this.low = void 0;
  6132. _this.open = void 0;
  6133. _this.options = void 0;
  6134. _this.plotClose = void 0;
  6135. _this.plotOpen = void 0;
  6136. _this.series = void 0;
  6137. return _this;
  6138. /* eslint-enable valid-jsdoc */
  6139. }
  6140. /* *
  6141. *
  6142. * Functions
  6143. *
  6144. * */
  6145. /* eslint-disable valid-jsdoc */
  6146. /**
  6147. * Extend the parent method by adding up or down to the class name.
  6148. * @private
  6149. * @function Highcharts.seriesTypes.ohlc#getClassName
  6150. * @return {string}
  6151. */
  6152. OHLCPoint.prototype.getClassName = function () {
  6153. return _super.prototype.getClassName.call(this) +
  6154. (this.open < this.close ?
  6155. ' highcharts-point-up' :
  6156. ' highcharts-point-down');
  6157. };
  6158. /**
  6159. * Save upColor as point color (#14826).
  6160. * @private
  6161. * @function Highcharts.seriesTypes.ohlc#resolveUpColor
  6162. */
  6163. OHLCPoint.prototype.resolveUpColor = function () {
  6164. if (this.open < this.close &&
  6165. !this.options.color &&
  6166. this.series.options.upColor) {
  6167. this.color = this.series.options.upColor;
  6168. }
  6169. };
  6170. /**
  6171. * Extend the parent method by saving upColor.
  6172. * @private
  6173. * @function Highcharts.seriesTypes.ohlc#resolveColor
  6174. */
  6175. OHLCPoint.prototype.resolveColor = function () {
  6176. _super.prototype.resolveColor.call(this);
  6177. this.resolveUpColor();
  6178. };
  6179. /**
  6180. * Extend the parent method by saving upColor.
  6181. * @private
  6182. * @function Highcharts.seriesTypes.ohlc#getZone
  6183. *
  6184. * @return {Highcharts.SeriesZonesOptionsObject}
  6185. * The zone item.
  6186. */
  6187. OHLCPoint.prototype.getZone = function () {
  6188. var zone = _super.prototype.getZone.call(this);
  6189. this.resolveUpColor();
  6190. return zone;
  6191. };
  6192. return OHLCPoint;
  6193. }(ColumnSeries.prototype.pointClass));
  6194. /* *
  6195. *
  6196. * Default Export
  6197. *
  6198. * */
  6199. return OHLCPoint;
  6200. });
  6201. _registerModule(_modules, 'Series/OHLC/OHLCSeries.js', [_modules['Series/OHLC/OHLCPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (OHLCPoint, SeriesRegistry, U) {
  6202. /* *
  6203. *
  6204. * (c) 2010-2021 Torstein Honsi
  6205. *
  6206. * License: www.highcharts.com/license
  6207. *
  6208. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6209. *
  6210. * */
  6211. var __extends = (this && this.__extends) || (function () {
  6212. var extendStatics = function (d,
  6213. b) {
  6214. extendStatics = Object.setPrototypeOf ||
  6215. ({ __proto__: [] } instanceof Array && function (d,
  6216. b) { d.__proto__ = b; }) ||
  6217. function (d,
  6218. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6219. return extendStatics(d, b);
  6220. };
  6221. return function (d, b) {
  6222. extendStatics(d, b);
  6223. function __() { this.constructor = d; }
  6224. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6225. };
  6226. })();
  6227. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6228. var extend = U.extend,
  6229. merge = U.merge;
  6230. /* *
  6231. *
  6232. * Class
  6233. *
  6234. * */
  6235. /**
  6236. * The ohlc series type.
  6237. *
  6238. * @private
  6239. * @class
  6240. * @name Highcharts.seriesTypes.ohlc
  6241. *
  6242. * @augments Highcharts.Series
  6243. */
  6244. var OHLCSeries = /** @class */ (function (_super) {
  6245. __extends(OHLCSeries, _super);
  6246. function OHLCSeries() {
  6247. /* *
  6248. *
  6249. * Static Properties
  6250. *
  6251. * */
  6252. var _this = _super !== null && _super.apply(this,
  6253. arguments) || this;
  6254. /* *
  6255. *
  6256. * Properties
  6257. *
  6258. * */
  6259. _this.data = void 0;
  6260. _this.options = void 0;
  6261. _this.points = void 0;
  6262. _this.yData = void 0;
  6263. return _this;
  6264. /* eslint-enable valid-jsdoc */
  6265. }
  6266. /* *
  6267. *
  6268. * Functions
  6269. *
  6270. * */
  6271. /* eslint-disable valid-jsdoc */
  6272. /**
  6273. * Draw the data points
  6274. * @private
  6275. */
  6276. OHLCSeries.prototype.drawPoints = function () {
  6277. var series = this,
  6278. points = series.points,
  6279. chart = series.chart,
  6280. /**
  6281. * Extend vertical stem to open and close values.
  6282. */
  6283. extendStem = function (path,
  6284. halfStrokeWidth,
  6285. openOrClose) {
  6286. var start = path[0];
  6287. var end = path[1];
  6288. // We don't need to worry about crisp - openOrClose value
  6289. // is already crisped and halfStrokeWidth should remove it.
  6290. if (typeof start[2] === 'number') {
  6291. start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
  6292. }
  6293. if (typeof end[2] === 'number') {
  6294. end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
  6295. }
  6296. };
  6297. points.forEach(function (point) {
  6298. var plotOpen,
  6299. plotClose,
  6300. crispCorr,
  6301. halfWidth,
  6302. path,
  6303. graphic = point.graphic,
  6304. crispX,
  6305. isNew = !graphic,
  6306. strokeWidth;
  6307. if (typeof point.plotY !== 'undefined') {
  6308. // Create and/or update the graphic
  6309. if (!graphic) {
  6310. point.graphic = graphic = chart.renderer.path()
  6311. .add(series.group);
  6312. }
  6313. if (!chart.styledMode) {
  6314. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  6315. }
  6316. // crisp vector coordinates
  6317. strokeWidth = graphic.strokeWidth();
  6318. crispCorr = (strokeWidth % 2) / 2;
  6319. // #2596:
  6320. crispX = Math.round(point.plotX) - crispCorr;
  6321. halfWidth = Math.round(point.shapeArgs.width / 2);
  6322. // the vertical stem
  6323. path = [
  6324. ['M', crispX, Math.round(point.yBottom)],
  6325. ['L', crispX, Math.round(point.plotHigh)]
  6326. ];
  6327. // open
  6328. if (point.open !== null) {
  6329. plotOpen = Math.round(point.plotOpen) + crispCorr;
  6330. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  6331. extendStem(path, strokeWidth / 2, plotOpen);
  6332. }
  6333. // close
  6334. if (point.close !== null) {
  6335. plotClose = Math.round(point.plotClose) + crispCorr;
  6336. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  6337. extendStem(path, strokeWidth / 2, plotClose);
  6338. }
  6339. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6340. .addClass(point.getClassName(), true);
  6341. }
  6342. });
  6343. };
  6344. /**
  6345. * @private
  6346. * @function Highcarts.seriesTypes.ohlc#init
  6347. * @return {void}
  6348. */
  6349. OHLCSeries.prototype.init = function () {
  6350. _super.prototype.init.apply(this, arguments);
  6351. this.options.stacking = void 0; // #8817
  6352. };
  6353. /**
  6354. * Postprocess mapping between options and SVG attributes
  6355. * @private
  6356. */
  6357. OHLCSeries.prototype.pointAttribs = function (point, state) {
  6358. var attribs = _super.prototype.pointAttribs.call(this,
  6359. point,
  6360. state),
  6361. options = this.options;
  6362. delete attribs.fill;
  6363. if (!point.options.color &&
  6364. options.upColor &&
  6365. point.open < point.close) {
  6366. attribs.stroke = options.upColor;
  6367. }
  6368. return attribs;
  6369. };
  6370. OHLCSeries.prototype.toYData = function (point) {
  6371. // return a plain array for speedy calculation
  6372. return [point.open, point.high, point.low, point.close];
  6373. };
  6374. /**
  6375. * Translate data points from raw values x and y to plotX and plotY
  6376. *
  6377. * @private
  6378. * @function Highcharts.seriesTypes.ohlc#translate
  6379. * @return {void}
  6380. */
  6381. OHLCSeries.prototype.translate = function () {
  6382. var series = this,
  6383. yAxis = series.yAxis,
  6384. hasModifyValue = !!series.modifyValue,
  6385. translated = [
  6386. 'plotOpen',
  6387. 'plotHigh',
  6388. 'plotLow',
  6389. 'plotClose',
  6390. 'yBottom'
  6391. ]; // translate OHLC for
  6392. _super.prototype.translate.apply(series);
  6393. // Do the translation
  6394. series.points.forEach(function (point) {
  6395. [point.open, point.high, point.low, point.close, point.low]
  6396. .forEach(function (value, i) {
  6397. if (value !== null) {
  6398. if (hasModifyValue) {
  6399. value = series.modifyValue(value);
  6400. }
  6401. point[translated[i]] =
  6402. yAxis.toPixels(value, true);
  6403. }
  6404. });
  6405. // Align the tooltip to the high value to avoid covering the
  6406. // point
  6407. point.tooltipPos[1] =
  6408. point.plotHigh + yAxis.pos - series.chart.plotTop;
  6409. });
  6410. };
  6411. /**
  6412. * An OHLC chart is a style of financial chart used to describe price
  6413. * movements over time. It displays open, high, low and close values per
  6414. * data point.
  6415. *
  6416. * @sample stock/demo/ohlc/
  6417. * OHLC chart
  6418. *
  6419. * @extends plotOptions.column
  6420. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  6421. * stack
  6422. * @product highstock
  6423. * @optionparent plotOptions.ohlc
  6424. */
  6425. OHLCSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  6426. /**
  6427. * The approximate pixel width of each group. If for example a series
  6428. * with 30 points is displayed over a 600 pixel wide plot area, no
  6429. * grouping is performed. If however the series contains so many points
  6430. * that the spacing is less than the groupPixelWidth, Highcharts will
  6431. * try to group it into appropriate groups so that each is more or less
  6432. * two pixels wide. Defaults to `5`.
  6433. *
  6434. * @type {number}
  6435. * @default 5
  6436. * @product highstock
  6437. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  6438. */
  6439. /**
  6440. * The pixel width of the line/border. Defaults to `1`.
  6441. *
  6442. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  6443. * A greater line width
  6444. *
  6445. * @type {number}
  6446. * @default 1
  6447. * @product highstock
  6448. *
  6449. * @private
  6450. */
  6451. lineWidth: 1,
  6452. tooltip: {
  6453. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  6454. '<b> {series.name}</b><br/>' +
  6455. 'Open: {point.open}<br/>' +
  6456. 'High: {point.high}<br/>' +
  6457. 'Low: {point.low}<br/>' +
  6458. 'Close: {point.close}<br/>'
  6459. },
  6460. threshold: null,
  6461. states: {
  6462. /**
  6463. * @extends plotOptions.column.states.hover
  6464. * @product highstock
  6465. */
  6466. hover: {
  6467. /**
  6468. * The pixel width of the line representing the OHLC point.
  6469. *
  6470. * @type {number}
  6471. * @default 3
  6472. * @product highstock
  6473. */
  6474. lineWidth: 3
  6475. }
  6476. },
  6477. /**
  6478. * Determines which one of `open`, `high`, `low`, `close` values should
  6479. * be represented as `point.y`, which is later used to set dataLabel
  6480. * position and [compare](#plotOptions.series.compare).
  6481. *
  6482. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  6483. * Possible values
  6484. *
  6485. * @type {string}
  6486. * @default close
  6487. * @validvalue ["open", "high", "low", "close"]
  6488. * @product highstock
  6489. * @apioption plotOptions.ohlc.pointValKey
  6490. */
  6491. /**
  6492. * @default close
  6493. * @apioption plotOptions.ohlc.colorKey
  6494. */
  6495. /**
  6496. * Line color for up points.
  6497. *
  6498. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6499. * @product highstock
  6500. * @apioption plotOptions.ohlc.upColor
  6501. */
  6502. stickyTracking: true
  6503. });
  6504. return OHLCSeries;
  6505. }(ColumnSeries));
  6506. extend(OHLCSeries.prototype, {
  6507. animate: null,
  6508. directTouch: false,
  6509. pointArrayMap: ['open', 'high', 'low', 'close'],
  6510. pointAttrToOptions: {
  6511. stroke: 'color',
  6512. 'stroke-width': 'lineWidth'
  6513. },
  6514. pointValKey: 'close'
  6515. });
  6516. OHLCSeries.prototype.pointClass = OHLCPoint;
  6517. SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
  6518. /* *
  6519. *
  6520. * Default Export
  6521. *
  6522. * */
  6523. /* *
  6524. *
  6525. * API Options
  6526. *
  6527. * */
  6528. /**
  6529. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  6530. * specified, it is inherited from [chart.type](#chart.type).
  6531. *
  6532. * @extends series,plotOptions.ohlc
  6533. * @excluding dataParser, dataURL
  6534. * @product highstock
  6535. * @apioption series.ohlc
  6536. */
  6537. /**
  6538. * An array of data points for the series. For the `ohlc` series type,
  6539. * points can be given in the following ways:
  6540. *
  6541. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6542. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6543. * as the name of the point, and the `x` value is inferred. The `x` value can
  6544. * also be omitted, in which case the inner arrays should be of length 4\.
  6545. * Then the `x` value is automatically calculated, either starting at 0 and
  6546. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6547. * series options.
  6548. * ```js
  6549. * data: [
  6550. * [0, 6, 5, 6, 7],
  6551. * [1, 9, 4, 8, 2],
  6552. * [2, 6, 3, 4, 10]
  6553. * ]
  6554. * ```
  6555. *
  6556. * 2. An array of objects with named values. The following snippet shows only a
  6557. * few settings, see the complete options set below. If the total number of
  6558. * data points exceeds the series'
  6559. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  6560. * available.
  6561. * ```js
  6562. * data: [{
  6563. * x: 1,
  6564. * open: 3,
  6565. * high: 4,
  6566. * low: 5,
  6567. * close: 2,
  6568. * name: "Point2",
  6569. * color: "#00FF00"
  6570. * }, {
  6571. * x: 1,
  6572. * open: 4,
  6573. * high: 3,
  6574. * low: 6,
  6575. * close: 7,
  6576. * name: "Point1",
  6577. * color: "#FF00FF"
  6578. * }]
  6579. * ```
  6580. *
  6581. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6582. * @extends series.arearange.data
  6583. * @excluding y, marker
  6584. * @product highstock
  6585. * @apioption series.ohlc.data
  6586. */
  6587. /**
  6588. * The closing value of each data point.
  6589. *
  6590. * @type {number}
  6591. * @product highstock
  6592. * @apioption series.ohlc.data.close
  6593. */
  6594. /**
  6595. * The opening value of each data point.
  6596. *
  6597. * @type {number}
  6598. * @product highstock
  6599. * @apioption series.ohlc.data.open
  6600. */
  6601. ''; // adds doclets above to transpilat
  6602. return OHLCSeries;
  6603. });
  6604. _registerModule(_modules, 'Series/Candlestick/CandlestickSeries.js', [_modules['Core/DefaultOptions.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (D, palette, SeriesRegistry, U) {
  6605. /* *
  6606. *
  6607. * (c) 2010-2021 Torstein Honsi
  6608. *
  6609. * License: www.highcharts.com/license
  6610. *
  6611. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6612. *
  6613. * */
  6614. var __extends = (this && this.__extends) || (function () {
  6615. var extendStatics = function (d,
  6616. b) {
  6617. extendStatics = Object.setPrototypeOf ||
  6618. ({ __proto__: [] } instanceof Array && function (d,
  6619. b) { d.__proto__ = b; }) ||
  6620. function (d,
  6621. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6622. return extendStatics(d, b);
  6623. };
  6624. return function (d, b) {
  6625. extendStatics(d, b);
  6626. function __() { this.constructor = d; }
  6627. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6628. };
  6629. })();
  6630. var defaultOptions = D.defaultOptions;
  6631. var _a = SeriesRegistry.seriesTypes,
  6632. ColumnSeries = _a.column,
  6633. OHLCSeries = _a.ohlc;
  6634. var merge = U.merge;
  6635. /* *
  6636. *
  6637. * Code
  6638. *
  6639. * */
  6640. /**
  6641. * The candlestick series type.
  6642. *
  6643. * @private
  6644. * @class
  6645. * @name Highcharts.seriesTypes.candlestick
  6646. *
  6647. * @augments Highcharts.seriesTypes.ohlc
  6648. */
  6649. var CandlestickSeries = /** @class */ (function (_super) {
  6650. __extends(CandlestickSeries, _super);
  6651. function CandlestickSeries() {
  6652. /* *
  6653. *
  6654. * Static properties
  6655. *
  6656. * */
  6657. var _this = _super !== null && _super.apply(this,
  6658. arguments) || this;
  6659. /* *
  6660. *
  6661. * Properties
  6662. *
  6663. * */
  6664. _this.data = void 0;
  6665. _this.options = void 0;
  6666. _this.points = void 0;
  6667. return _this;
  6668. }
  6669. /* *
  6670. *
  6671. * Functions
  6672. *
  6673. * */
  6674. /* eslint-disable valid-jsdoc */
  6675. /**
  6676. * Postprocess mapping between options and SVG attributes
  6677. *
  6678. * @private
  6679. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  6680. */
  6681. CandlestickSeries.prototype.pointAttribs = function (point, state) {
  6682. var attribs = ColumnSeries.prototype.pointAttribs.call(this,
  6683. point,
  6684. state),
  6685. options = this.options,
  6686. isUp = point.open < point.close,
  6687. stroke = options.lineColor || this.color,
  6688. color = point.color || this.color, // (#14826)
  6689. stateOptions;
  6690. attribs['stroke-width'] = options.lineWidth;
  6691. attribs.fill = point.options.color ||
  6692. (isUp ? (options.upColor || color) : color);
  6693. attribs.stroke = point.options.lineColor ||
  6694. (isUp ? (options.upLineColor || stroke) : stroke);
  6695. // Select or hover states
  6696. if (state) {
  6697. stateOptions = options.states[state];
  6698. attribs.fill = stateOptions.color || attribs.fill;
  6699. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  6700. attribs['stroke-width'] =
  6701. stateOptions.lineWidth || attribs['stroke-width'];
  6702. }
  6703. return attribs;
  6704. };
  6705. /**
  6706. * Draw the data points.
  6707. *
  6708. * @private
  6709. * @function Highcharts.seriesTypes.candlestick#drawPoints
  6710. * @return {void}
  6711. */
  6712. CandlestickSeries.prototype.drawPoints = function () {
  6713. var series = this,
  6714. points = series.points,
  6715. chart = series.chart,
  6716. reversedYAxis = series.yAxis.reversed;
  6717. points.forEach(function (point) {
  6718. var graphic = point.graphic,
  6719. plotOpen,
  6720. plotClose,
  6721. topBox,
  6722. bottomBox,
  6723. hasTopWhisker,
  6724. hasBottomWhisker,
  6725. crispCorr,
  6726. crispX,
  6727. path,
  6728. halfWidth,
  6729. isNew = !graphic;
  6730. if (typeof point.plotY !== 'undefined') {
  6731. if (!graphic) {
  6732. point.graphic = graphic = chart.renderer.path()
  6733. .add(series.group);
  6734. }
  6735. if (!series.chart.styledMode) {
  6736. graphic
  6737. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  6738. .shadow(series.options.shadow);
  6739. }
  6740. // Crisp vector coordinates
  6741. crispCorr = (graphic.strokeWidth() % 2) / 2;
  6742. // #2596:
  6743. crispX = Math.round(point.plotX) - crispCorr;
  6744. plotOpen = point.plotOpen;
  6745. plotClose = point.plotClose;
  6746. topBox = Math.min(plotOpen, plotClose);
  6747. bottomBox = Math.max(plotOpen, plotClose);
  6748. halfWidth = Math.round(point.shapeArgs.width / 2);
  6749. hasTopWhisker = reversedYAxis ?
  6750. bottomBox !== point.yBottom :
  6751. Math.round(topBox) !==
  6752. Math.round(point.plotHigh);
  6753. hasBottomWhisker = reversedYAxis ?
  6754. Math.round(topBox) !==
  6755. Math.round(point.plotHigh) :
  6756. bottomBox !== point.yBottom;
  6757. topBox = Math.round(topBox) + crispCorr;
  6758. bottomBox = Math.round(bottomBox) + crispCorr;
  6759. // Create the path. Due to a bug in Chrome 49, the path is
  6760. // first instanciated with no values, then the values
  6761. // pushed. For unknown reasons, instanciating the path array
  6762. // with all the values would lead to a crash when updating
  6763. // frequently (#5193).
  6764. path = [];
  6765. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  6766. ['M', crispX, topBox], [
  6767. 'L',
  6768. // #460, #2094
  6769. crispX,
  6770. hasTopWhisker ?
  6771. Math.round(reversedYAxis ?
  6772. point.yBottom :
  6773. point.plotHigh) :
  6774. topBox
  6775. ], ['M', crispX, bottomBox], [
  6776. 'L',
  6777. // #460, #2094
  6778. crispX,
  6779. hasBottomWhisker ?
  6780. Math.round(reversedYAxis ?
  6781. point.plotHigh :
  6782. point.yBottom) :
  6783. bottomBox
  6784. ]);
  6785. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6786. .addClass(point.getClassName(), true);
  6787. }
  6788. });
  6789. /* eslint-enable valid-jsdoc */
  6790. };
  6791. /**
  6792. * A candlestick chart is a style of financial chart used to describe price
  6793. * movements over time.
  6794. *
  6795. * @sample stock/demo/candlestick/
  6796. * Candlestick chart
  6797. *
  6798. * @extends plotOptions.ohlc
  6799. * @excluding borderColor,borderRadius,borderWidth
  6800. * @product highstock
  6801. * @optionparent plotOptions.candlestick
  6802. */
  6803. CandlestickSeries.defaultOptions = merge(OHLCSeries.defaultOptions, defaultOptions.plotOptions, {
  6804. /**
  6805. * The specific line color for up candle sticks. The default is to
  6806. * inherit the general `lineColor` setting.
  6807. *
  6808. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6809. * Candlestick line colors
  6810. *
  6811. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6812. * @since 1.3.6
  6813. * @product highstock
  6814. * @apioption plotOptions.candlestick.upLineColor
  6815. */
  6816. /**
  6817. * @type {Highcharts.DataGroupingApproximationValue|Function}
  6818. * @default ohlc
  6819. * @product highstock
  6820. * @apioption plotOptions.candlestick.dataGrouping.approximation
  6821. */
  6822. states: {
  6823. /**
  6824. * @extends plotOptions.column.states.hover
  6825. * @product highstock
  6826. */
  6827. hover: {
  6828. /**
  6829. * The pixel width of the line/border around the candlestick.
  6830. *
  6831. * @product highstock
  6832. */
  6833. lineWidth: 2
  6834. }
  6835. },
  6836. /**
  6837. * @extends plotOptions.ohlc.tooltip
  6838. */
  6839. tooltip: defaultOptions.plotOptions.ohlc.tooltip,
  6840. /**
  6841. * @type {number|null}
  6842. * @product highstock
  6843. */
  6844. threshold: null,
  6845. /**
  6846. * The color of the line/border of the candlestick.
  6847. *
  6848. * In styled mode, the line stroke can be set with the
  6849. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6850. *
  6851. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  6852. *
  6853. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6854. * Candlestick line colors
  6855. *
  6856. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6857. * @default #000000
  6858. * @product highstock
  6859. */
  6860. lineColor: palette.neutralColor100,
  6861. /**
  6862. * The pixel width of the candlestick line/border. Defaults to `1`.
  6863. *
  6864. *
  6865. * In styled mode, the line stroke width can be set with the
  6866. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6867. *
  6868. * @product highstock
  6869. */
  6870. lineWidth: 1,
  6871. /**
  6872. * The fill color of the candlestick when values are rising.
  6873. *
  6874. * In styled mode, the up color can be set with the
  6875. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  6876. *
  6877. * @sample {highstock} stock/plotoptions/candlestick-color/
  6878. * Custom colors
  6879. * @sample {highstock} highcharts/css/candlestick/
  6880. * Colors in styled mode
  6881. *
  6882. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6883. * @default #ffffff
  6884. * @product highstock
  6885. */
  6886. upColor: palette.backgroundColor,
  6887. /**
  6888. * @product highstock
  6889. */
  6890. stickyTracking: true
  6891. });
  6892. return CandlestickSeries;
  6893. }(OHLCSeries));
  6894. SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
  6895. /* *
  6896. *
  6897. * Default Export
  6898. *
  6899. * */
  6900. /* *
  6901. *
  6902. * API Options
  6903. *
  6904. * */
  6905. /**
  6906. * A `candlestick` series. If the [type](#series.candlestick.type)
  6907. * option is not specified, it is inherited from [chart.type](
  6908. * #chart.type).
  6909. *
  6910. * @type {*}
  6911. * @extends series,plotOptions.candlestick
  6912. * @excluding dataParser, dataURL, marker
  6913. * @product highstock
  6914. * @apioption series.candlestick
  6915. */
  6916. /**
  6917. * An array of data points for the series. For the `candlestick` series
  6918. * type, points can be given in the following ways:
  6919. *
  6920. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6921. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6922. * as the name of the point, and the `x` value is inferred. The `x` value can
  6923. * also be omitted, in which case the inner arrays should be of length 4.
  6924. * Then the `x` value is automatically calculated, either starting at 0 and
  6925. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6926. * series options.
  6927. * ```js
  6928. * data: [
  6929. * [0, 7, 2, 0, 4],
  6930. * [1, 1, 4, 2, 8],
  6931. * [2, 3, 3, 9, 3]
  6932. * ]
  6933. * ```
  6934. *
  6935. * 2. An array of objects with named values. The following snippet shows only a
  6936. * few settings, see the complete options set below. If the total number of
  6937. * data points exceeds the series'
  6938. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  6939. * available.
  6940. * ```js
  6941. * data: [{
  6942. * x: 1,
  6943. * open: 9,
  6944. * high: 2,
  6945. * low: 4,
  6946. * close: 6,
  6947. * name: "Point2",
  6948. * color: "#00FF00"
  6949. * }, {
  6950. * x: 1,
  6951. * open: 1,
  6952. * high: 4,
  6953. * low: 7,
  6954. * close: 7,
  6955. * name: "Point1",
  6956. * color: "#FF00FF"
  6957. * }]
  6958. * ```
  6959. *
  6960. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6961. * @extends series.ohlc.data
  6962. * @excluding y
  6963. * @product highstock
  6964. * @apioption series.candlestick.data
  6965. */
  6966. ''; // adds doclets above to transpilat
  6967. return CandlestickSeries;
  6968. });
  6969. _registerModule(_modules, 'Series/Flags/FlagsPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  6970. /* *
  6971. *
  6972. * (c) 2010-2021 Torstein Honsi
  6973. *
  6974. * License: www.highcharts.com/license
  6975. *
  6976. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6977. *
  6978. * */
  6979. var __extends = (this && this.__extends) || (function () {
  6980. var extendStatics = function (d,
  6981. b) {
  6982. extendStatics = Object.setPrototypeOf ||
  6983. ({ __proto__: [] } instanceof Array && function (d,
  6984. b) { d.__proto__ = b; }) ||
  6985. function (d,
  6986. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6987. return extendStatics(d, b);
  6988. };
  6989. return function (d, b) {
  6990. extendStatics(d, b);
  6991. function __() { this.constructor = d; }
  6992. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6993. };
  6994. })();
  6995. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6996. var isNumber = U.isNumber;
  6997. /* *
  6998. *
  6999. * Class
  7000. *
  7001. * */
  7002. var FlagsPoint = /** @class */ (function (_super) {
  7003. __extends(FlagsPoint, _super);
  7004. function FlagsPoint() {
  7005. /* *
  7006. *
  7007. * Properties
  7008. *
  7009. * */
  7010. var _this = _super !== null && _super.apply(this,
  7011. arguments) || this;
  7012. _this.options = void 0;
  7013. _this.series = void 0;
  7014. return _this;
  7015. }
  7016. /* *
  7017. *
  7018. * Functions
  7019. *
  7020. * */
  7021. /* eslint-disable valid-jsdoc */
  7022. /**
  7023. * @private
  7024. */
  7025. FlagsPoint.prototype.isValid = function () {
  7026. // #9233 - Prevent from treating flags as null points (even if
  7027. // they have no y values defined).
  7028. return isNumber(this.y) || typeof this.y === 'undefined';
  7029. };
  7030. /**
  7031. * @private
  7032. */
  7033. FlagsPoint.prototype.hasNewShapeType = function () {
  7034. var shape = this.options.shape || this.series.options.shape;
  7035. return this.graphic && shape && shape !== this.graphic.symbolKey;
  7036. };
  7037. return FlagsPoint;
  7038. }(ColumnSeries.prototype.pointClass));
  7039. /* *
  7040. *
  7041. * Default Export
  7042. *
  7043. * */
  7044. return FlagsPoint;
  7045. });
  7046. _registerModule(_modules, 'Mixins/OnSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (ColumnSeries, Series, U) {
  7047. /* *
  7048. *
  7049. * (c) 2010-2021 Torstein Honsi
  7050. *
  7051. * License: www.highcharts.com/license
  7052. *
  7053. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7054. *
  7055. * */
  7056. var columnProto = ColumnSeries.prototype;
  7057. var seriesProto = Series.prototype;
  7058. var defined = U.defined,
  7059. stableSort = U.stableSort;
  7060. /**
  7061. * @private
  7062. * @mixin onSeriesMixin
  7063. */
  7064. var onSeriesMixin = {
  7065. /* eslint-disable valid-jsdoc */
  7066. /**
  7067. * Override getPlotBox. If the onSeries option is valid,
  7068. return the plot box
  7069. * of the onSeries,
  7070. otherwise proceed as usual.
  7071. *
  7072. * @private
  7073. * @function onSeriesMixin.getPlotBox
  7074. * @return {Highcharts.SeriesPlotBoxObject}
  7075. */
  7076. getPlotBox: function () {
  7077. return seriesProto.getPlotBox.call((this.options.onSeries &&
  7078. this.chart.get(this.options.onSeries)) || this);
  7079. },
  7080. /**
  7081. * Extend the translate method by placing the point on the related series
  7082. *
  7083. * @private
  7084. * @function onSeriesMixin.translate
  7085. * @return {void}
  7086. */
  7087. translate: function () {
  7088. columnProto.translate.apply(this);
  7089. var series = this,
  7090. options = series.options,
  7091. chart = series.chart,
  7092. points = series.points,
  7093. cursor = points.length - 1,
  7094. point,
  7095. lastPoint,
  7096. optionsOnSeries = options.onSeries,
  7097. onSeries = (optionsOnSeries &&
  7098. chart.get(optionsOnSeries)),
  7099. onKey = options.onKey || 'y',
  7100. step = onSeries && onSeries.options.step,
  7101. onData = (onSeries && onSeries.points),
  7102. i = onData && onData.length,
  7103. inverted = chart.inverted,
  7104. xAxis = series.xAxis,
  7105. yAxis = series.yAxis,
  7106. xOffset = 0,
  7107. leftPoint,
  7108. lastX,
  7109. rightPoint,
  7110. currentDataGrouping,
  7111. distanceRatio;
  7112. // relate to a master series
  7113. if (onSeries && onSeries.visible && i) {
  7114. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  7115. currentDataGrouping = onSeries.currentDataGrouping;
  7116. lastX = (onData[i - 1].x +
  7117. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  7118. // sort the data points
  7119. stableSort(points, function (a, b) {
  7120. return (a.x - b.x);
  7121. });
  7122. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  7123. while (i-- && points[cursor]) {
  7124. leftPoint = onData[i];
  7125. point = points[cursor];
  7126. point.y = leftPoint.y;
  7127. if (leftPoint.x <= point.x &&
  7128. typeof leftPoint[onKey] !== 'undefined') {
  7129. if (point.x <= lastX) { // #803
  7130. point.plotY = leftPoint[onKey];
  7131. // interpolate between points, #666
  7132. if (leftPoint.x < point.x &&
  7133. !step) {
  7134. rightPoint = onData[i + 1];
  7135. if (rightPoint &&
  7136. typeof rightPoint[onKey] !== 'undefined') {
  7137. // the distance ratio, between 0 and 1
  7138. distanceRatio =
  7139. (point.x - leftPoint.x) /
  7140. (rightPoint.x - leftPoint.x);
  7141. point.plotY +=
  7142. distanceRatio *
  7143. // the plotY distance
  7144. (rightPoint[onKey] - leftPoint[onKey]);
  7145. point.y +=
  7146. distanceRatio *
  7147. (rightPoint.y - leftPoint.y);
  7148. }
  7149. }
  7150. }
  7151. cursor--;
  7152. i++; // check again for points in the same x position
  7153. if (cursor < 0) {
  7154. break;
  7155. }
  7156. }
  7157. }
  7158. }
  7159. // Add plotY position and handle stacking
  7160. points.forEach(function (point, i) {
  7161. var stackIndex;
  7162. point.plotX += xOffset; // #2049
  7163. // Undefined plotY means the point is either on axis, outside series
  7164. // range or hidden series. If the series is outside the range of the
  7165. // x axis it should fall through with an undefined plotY, but then
  7166. // we must remove the shapeArgs (#847). For inverted charts, we need
  7167. // to calculate position anyway, because series.invertGroups is not
  7168. // defined
  7169. if (typeof point.plotY === 'undefined' || inverted) {
  7170. if (point.plotX >= 0 &&
  7171. point.plotX <= xAxis.len) {
  7172. // We're inside xAxis range
  7173. if (inverted) {
  7174. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  7175. point.plotX = defined(point.y) ?
  7176. yAxis.translate(point.y, 0, 0, 0, 1) :
  7177. 0;
  7178. }
  7179. else {
  7180. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  7181. xAxis.offset; // For the windbarb demo
  7182. }
  7183. }
  7184. else {
  7185. point.shapeArgs = {}; // 847
  7186. }
  7187. }
  7188. // if multiple flags appear at the same x, order them into a stack
  7189. lastPoint = points[i - 1];
  7190. if (lastPoint && lastPoint.plotX === point.plotX) {
  7191. if (typeof lastPoint.stackIndex === 'undefined') {
  7192. lastPoint.stackIndex = 0;
  7193. }
  7194. stackIndex = lastPoint.stackIndex + 1;
  7195. }
  7196. point.stackIndex = stackIndex; // #3639
  7197. });
  7198. this.onSeries = onSeries;
  7199. }
  7200. /* eslint-enable valid-jsdoc */
  7201. };
  7202. return onSeriesMixin;
  7203. });
  7204. _registerModule(_modules, 'Series/Flags/FlagsSymbols.js', [_modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Renderer/SVG/SVGRenderer.js']], function (RendererRegistry, SVGRenderer) {
  7205. /* *
  7206. *
  7207. * Imports
  7208. *
  7209. * */
  7210. var symbols = SVGRenderer.prototype.symbols;
  7211. /* *
  7212. *
  7213. * Symbols
  7214. *
  7215. * */
  7216. // create the flag icon with anchor
  7217. symbols.flag = function (x, y, w, h, options) {
  7218. var anchorX = (options && options.anchorX) || x,
  7219. anchorY = (options && options.anchorY) || y;
  7220. // To do: unwanted any cast because symbols.circle has wrong type, it
  7221. // actually returns an SVGPathArray
  7222. var path = symbols.circle(anchorX - 1,
  7223. anchorY - 1, 2, 2);
  7224. path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
  7225. return path;
  7226. };
  7227. /**
  7228. * Create the circlepin and squarepin icons with anchor.
  7229. * @private
  7230. * @param {string} shape - circle or square
  7231. * @return {void}
  7232. */
  7233. function createPinSymbol(shape) {
  7234. symbols[(shape + 'pin')] = function (x, y, w, h, options) {
  7235. var anchorX = options && options.anchorX,
  7236. anchorY = options && options.anchorY;
  7237. var path;
  7238. // For single-letter flags, make sure circular flags are not taller
  7239. // than their width
  7240. if (shape === 'circle' && h > w) {
  7241. x -= Math.round((h - w) / 2);
  7242. w = h;
  7243. }
  7244. path = (symbols[shape])(x, y, w, h);
  7245. if (anchorX && anchorY) {
  7246. /**
  7247. * If the label is below the anchor, draw the connecting line from
  7248. * the top edge of the label, otherwise start drawing from the
  7249. * bottom edge
  7250. */
  7251. var labelX = anchorX;
  7252. if (shape === 'circle') {
  7253. labelX = x + w / 2;
  7254. }
  7255. else {
  7256. var startSeg = path[0];
  7257. var endSeg = path[1];
  7258. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  7259. labelX = (startSeg[1] + endSeg[1]) / 2;
  7260. }
  7261. }
  7262. var labelY = (y > anchorY) ? y : y + h;
  7263. path.push([
  7264. 'M',
  7265. labelX,
  7266. labelY
  7267. ], [
  7268. 'L',
  7269. anchorX,
  7270. anchorY
  7271. ]);
  7272. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  7273. }
  7274. return path;
  7275. };
  7276. }
  7277. createPinSymbol('circle');
  7278. createPinSymbol('square');
  7279. /**
  7280. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  7281. * Even VML browsers need this in order to generate shapes in export. Now share
  7282. * them with the VMLRenderer.
  7283. */
  7284. var Renderer = RendererRegistry.getRendererType();
  7285. if (Renderer !== SVGRenderer) {
  7286. Renderer.prototype.symbols.circlepin = symbols.circlepin;
  7287. Renderer.prototype.symbols.flag = symbols.flag;
  7288. Renderer.prototype.symbols.squarepin = symbols.squarepin;
  7289. }
  7290. /* *
  7291. *
  7292. * Default Export
  7293. *
  7294. * */
  7295. return symbols;
  7296. });
  7297. _registerModule(_modules, 'Series/Flags/FlagsSeries.js', [_modules['Series/Flags/FlagsPoint.js'], _modules['Core/Globals.js'], _modules['Mixins/OnSeries.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (FlagsPoint, H, OnSeriesMixin, palette, SeriesRegistry, SVGElement, U) {
  7298. /* *
  7299. *
  7300. * (c) 2010-2021 Torstein Honsi
  7301. *
  7302. * License: www.highcharts.com/license
  7303. *
  7304. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7305. *
  7306. * */
  7307. var __extends = (this && this.__extends) || (function () {
  7308. var extendStatics = function (d,
  7309. b) {
  7310. extendStatics = Object.setPrototypeOf ||
  7311. ({ __proto__: [] } instanceof Array && function (d,
  7312. b) { d.__proto__ = b; }) ||
  7313. function (d,
  7314. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7315. return extendStatics(d, b);
  7316. };
  7317. return function (d, b) {
  7318. extendStatics(d, b);
  7319. function __() { this.constructor = d; }
  7320. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  7321. };
  7322. })();
  7323. var noop = H.noop;
  7324. var Series = SeriesRegistry.series,
  7325. ColumnSeries = SeriesRegistry.seriesTypes.column;
  7326. var addEvent = U.addEvent,
  7327. defined = U.defined,
  7328. extend = U.extend,
  7329. merge = U.merge,
  7330. objectEach = U.objectEach,
  7331. wrap = U.wrap;
  7332. /**
  7333. * The Flags series.
  7334. *
  7335. * @private
  7336. * @class
  7337. * @name Highcharts.seriesTypes.flags
  7338. *
  7339. * @augments Highcharts.Series
  7340. */
  7341. var FlagsSeries = /** @class */ (function (_super) {
  7342. __extends(FlagsSeries, _super);
  7343. function FlagsSeries() {
  7344. /* *
  7345. *
  7346. * Static Properties
  7347. *
  7348. * */
  7349. var _this = _super !== null && _super.apply(this,
  7350. arguments) || this;
  7351. /* *
  7352. *
  7353. * Properties
  7354. *
  7355. * */
  7356. _this.data = void 0;
  7357. _this.options = void 0;
  7358. _this.points = void 0;
  7359. return _this;
  7360. /* eslint-enable valid-jsdoc */
  7361. }
  7362. /* *
  7363. *
  7364. * Functions
  7365. *
  7366. * */
  7367. /* eslint-disable valid-jsdoc */
  7368. /**
  7369. * Disable animation, but keep clipping (#8546).
  7370. * @private
  7371. */
  7372. FlagsSeries.prototype.animate = function (init) {
  7373. if (init) {
  7374. this.setClip();
  7375. }
  7376. };
  7377. /**
  7378. * Draw the markers.
  7379. * @private
  7380. */
  7381. FlagsSeries.prototype.drawPoints = function () {
  7382. var series = this,
  7383. points = series.points,
  7384. chart = series.chart,
  7385. renderer = chart.renderer,
  7386. plotX,
  7387. plotY,
  7388. inverted = chart.inverted,
  7389. options = series.options,
  7390. optionsY = options.y,
  7391. shape,
  7392. i,
  7393. point,
  7394. graphic,
  7395. stackIndex,
  7396. anchorY,
  7397. attribs,
  7398. outsideRight,
  7399. yAxis = series.yAxis,
  7400. boxesMap = {},
  7401. boxes = [],
  7402. centered;
  7403. i = points.length;
  7404. while (i--) {
  7405. point = points[i];
  7406. outsideRight =
  7407. (inverted ? point.plotY : point.plotX) >
  7408. series.xAxis.len;
  7409. plotX = point.plotX;
  7410. stackIndex = point.stackIndex;
  7411. shape = point.options.shape || options.shape;
  7412. plotY = point.plotY;
  7413. if (typeof plotY !== 'undefined') {
  7414. plotY = point.plotY + optionsY -
  7415. (typeof stackIndex !== 'undefined' &&
  7416. (stackIndex * options.stackDistance));
  7417. }
  7418. // skip connectors for higher level stacked points
  7419. point.anchorX = stackIndex ? void 0 : point.plotX;
  7420. anchorY = stackIndex ? void 0 : point.plotY;
  7421. centered = shape !== 'flag';
  7422. graphic = point.graphic;
  7423. // Only draw the point if y is defined and the flag is within
  7424. // the visible area
  7425. if (typeof plotY !== 'undefined' &&
  7426. plotX >= 0 &&
  7427. !outsideRight) {
  7428. // #15384
  7429. if (graphic && point.hasNewShapeType()) {
  7430. graphic = graphic.destroy();
  7431. }
  7432. // Create the flag
  7433. if (!graphic) {
  7434. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML)
  7435. .addClass('highcharts-point')
  7436. .add(series.markerGroup);
  7437. // Add reference to the point for tracker (#6303)
  7438. if (point.graphic.div) {
  7439. point.graphic.div.point = point;
  7440. }
  7441. graphic.isNew = true;
  7442. }
  7443. graphic.attr({
  7444. align: centered ? 'center' : 'left',
  7445. width: options.width,
  7446. height: options.height,
  7447. 'text-align': options.textAlign
  7448. });
  7449. if (!chart.styledMode) {
  7450. graphic
  7451. .attr(series.pointAttribs(point))
  7452. .css(merge(options.style, point.style))
  7453. .shadow(options.shadow);
  7454. }
  7455. if (plotX > 0) { // #3119
  7456. plotX -= graphic.strokeWidth() % 2; // #4285
  7457. }
  7458. // Plant the flag
  7459. attribs = {
  7460. y: plotY,
  7461. anchorY: anchorY
  7462. };
  7463. if (options.allowOverlapX) {
  7464. attribs.x = plotX;
  7465. attribs.anchorX = point.anchorX;
  7466. }
  7467. graphic.attr({
  7468. text: point.options.title || options.title || 'A'
  7469. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  7470. // Rig for the distribute function
  7471. if (!options.allowOverlapX) {
  7472. if (!boxesMap[point.plotX]) {
  7473. boxesMap[point.plotX] = {
  7474. align: centered ? 0.5 : 0,
  7475. size: graphic.width,
  7476. target: plotX,
  7477. anchorX: plotX
  7478. };
  7479. }
  7480. else {
  7481. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  7482. }
  7483. }
  7484. // Set the tooltip anchor position
  7485. point.tooltipPos = [
  7486. plotX,
  7487. plotY + yAxis.pos - chart.plotTop
  7488. ]; // #6327
  7489. }
  7490. else if (graphic) {
  7491. point.graphic = graphic.destroy();
  7492. }
  7493. }
  7494. // Handle X-dimension overlapping
  7495. if (!options.allowOverlapX) {
  7496. objectEach(boxesMap, function (box) {
  7497. box.plotX = box.anchorX;
  7498. boxes.push(box);
  7499. });
  7500. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  7501. points.forEach(function (point) {
  7502. var box = point.graphic && boxesMap[point.plotX];
  7503. if (box) {
  7504. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  7505. x: box.pos + box.align * box.size,
  7506. anchorX: point.anchorX
  7507. });
  7508. // Hide flag when its box position is not specified
  7509. // (#8573, #9299)
  7510. if (!defined(box.pos)) {
  7511. point.graphic.attr({
  7512. x: -9999,
  7513. anchorX: -9999
  7514. });
  7515. point.graphic.isNew = true;
  7516. }
  7517. else {
  7518. point.graphic.isNew = false;
  7519. }
  7520. }
  7521. });
  7522. }
  7523. // Can be a mix of SVG and HTML and we need events for both (#6303)
  7524. if (options.useHTML) {
  7525. wrap(series.markerGroup, 'on', function (proceed) {
  7526. return SVGElement.prototype.on.apply(
  7527. // for HTML
  7528. // eslint-disable-next-line no-invalid-this
  7529. proceed.apply(this, [].slice.call(arguments, 1)),
  7530. // and for SVG
  7531. [].slice.call(arguments, 1));
  7532. });
  7533. }
  7534. };
  7535. /**
  7536. * Extend the column trackers with listeners to expand and contract
  7537. * stacks.
  7538. * @private
  7539. */
  7540. FlagsSeries.prototype.drawTracker = function () {
  7541. var series = this,
  7542. points = series.points;
  7543. _super.prototype.drawTracker.call(this);
  7544. /* *
  7545. * Bring each stacked flag up on mouse over, this allows readability
  7546. * of vertically stacked elements as well as tight points on the x
  7547. * axis. #1924.
  7548. */
  7549. points.forEach(function (point) {
  7550. var graphic = point.graphic;
  7551. if (graphic) {
  7552. if (point.unbindMouseOver) {
  7553. point.unbindMouseOver();
  7554. }
  7555. point.unbindMouseOver = addEvent(graphic.element, 'mouseover', function () {
  7556. // Raise this point
  7557. if (point.stackIndex > 0 &&
  7558. !point.raised) {
  7559. point._y = graphic.y;
  7560. graphic.attr({
  7561. y: point._y - 8
  7562. });
  7563. point.raised = true;
  7564. }
  7565. // Revert other raised points
  7566. points.forEach(function (otherPoint) {
  7567. if (otherPoint !== point &&
  7568. otherPoint.raised &&
  7569. otherPoint.graphic) {
  7570. otherPoint.graphic.attr({
  7571. y: otherPoint._y
  7572. });
  7573. otherPoint.raised = false;
  7574. }
  7575. });
  7576. });
  7577. }
  7578. });
  7579. };
  7580. /**
  7581. * Get presentational attributes
  7582. * @private
  7583. */
  7584. FlagsSeries.prototype.pointAttribs = function (point, state) {
  7585. var options = this.options,
  7586. color = (point && point.color) || this.color,
  7587. lineColor = options.lineColor,
  7588. lineWidth = (point && point.lineWidth),
  7589. fill = (point && point.fillColor) || options.fillColor;
  7590. if (state) {
  7591. fill = options.states[state].fillColor;
  7592. lineColor = options.states[state].lineColor;
  7593. lineWidth = options.states[state].lineWidth;
  7594. }
  7595. return {
  7596. fill: fill || color,
  7597. stroke: lineColor || color,
  7598. 'stroke-width': lineWidth || options.lineWidth || 0
  7599. };
  7600. };
  7601. /**
  7602. * @private
  7603. */
  7604. FlagsSeries.prototype.setClip = function () {
  7605. Series.prototype.setClip.apply(this, arguments);
  7606. if (this.options.clip !== false &&
  7607. this.sharedClipKey &&
  7608. this.markerGroup) {
  7609. this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
  7610. }
  7611. };
  7612. /**
  7613. * Flags are used to mark events in stock charts. They can be added on the
  7614. * timeline, or attached to a specific series.
  7615. *
  7616. * @sample stock/demo/flags-general/
  7617. * Flags on a line series
  7618. *
  7619. * @extends plotOptions.column
  7620. * @excluding animation, borderColor, borderRadius, borderWidth,
  7621. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  7622. * turboThreshold
  7623. * @product highstock
  7624. * @optionparent plotOptions.flags
  7625. */
  7626. FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  7627. /**
  7628. * In case the flag is placed on a series, on what point key to place
  7629. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  7630. * however, the flag can optionally be placed on the `open`, `high`,
  7631. * `low` or `close` key.
  7632. *
  7633. * @sample {highstock} stock/plotoptions/flags-onkey/
  7634. * Range series, flag on high
  7635. *
  7636. * @type {string}
  7637. * @default y
  7638. * @since 4.2.2
  7639. * @product highstock
  7640. * @validvalue ["y", "open", "high", "low", "close"]
  7641. * @apioption plotOptions.flags.onKey
  7642. */
  7643. /**
  7644. * The id of the series that the flags should be drawn on. If no id
  7645. * is given, the flags are drawn on the x axis.
  7646. *
  7647. * @sample {highstock} stock/plotoptions/flags/
  7648. * Flags on series and on x axis
  7649. *
  7650. * @type {string}
  7651. * @product highstock
  7652. * @apioption plotOptions.flags.onSeries
  7653. */
  7654. pointRange: 0,
  7655. /**
  7656. * Whether the flags are allowed to overlap sideways. If `false`, the
  7657. * flags are moved sideways using an algorithm that seeks to place every
  7658. * flag as close as possible to its original position.
  7659. *
  7660. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  7661. * Allow sideways overlap
  7662. *
  7663. * @since 6.0.4
  7664. */
  7665. allowOverlapX: false,
  7666. /**
  7667. * The shape of the marker. Can be one of "flag", "circlepin",
  7668. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  7669. * Individual shapes can also be set for each point.
  7670. *
  7671. * @sample {highstock} stock/plotoptions/flags/
  7672. * Different shapes
  7673. *
  7674. * @type {Highcharts.FlagsShapeValue}
  7675. * @product highstock
  7676. */
  7677. shape: 'flag',
  7678. /**
  7679. * When multiple flags in the same series fall on the same value, this
  7680. * number determines the vertical offset between them.
  7681. *
  7682. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  7683. * A greater stack distance
  7684. *
  7685. * @product highstock
  7686. */
  7687. stackDistance: 12,
  7688. /**
  7689. * Text alignment for the text inside the flag.
  7690. *
  7691. * @since 5.0.0
  7692. * @product highstock
  7693. * @validvalue ["left", "center", "right"]
  7694. */
  7695. textAlign: 'center',
  7696. /**
  7697. * Specific tooltip options for flag series. Flag series tooltips are
  7698. * different from most other types in that a flag doesn't have a data
  7699. * value, so the tooltip rather displays the `text` option for each
  7700. * point.
  7701. *
  7702. * @extends plotOptions.series.tooltip
  7703. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  7704. * @product highstock
  7705. */
  7706. tooltip: {
  7707. pointFormat: '{point.text}'
  7708. },
  7709. threshold: null,
  7710. /**
  7711. * The text to display on each flag. This can be defined on series
  7712. * level, or individually for each point. Defaults to `"A"`.
  7713. *
  7714. * @type {string}
  7715. * @default A
  7716. * @product highstock
  7717. * @apioption plotOptions.flags.title
  7718. */
  7719. /**
  7720. * The y position of the top left corner of the flag relative to either
  7721. * the series (if onSeries is defined), or the x axis. Defaults to
  7722. * `-30`.
  7723. *
  7724. * @product highstock
  7725. */
  7726. y: -30,
  7727. /**
  7728. * Whether to use HTML to render the flag texts. Using HTML allows for
  7729. * advanced formatting, images and reliable bi-directional text
  7730. * rendering. Note that exported images won't respect the HTML, and that
  7731. * HTML won't respect Z-index settings.
  7732. *
  7733. * @type {boolean}
  7734. * @default false
  7735. * @since 1.3
  7736. * @product highstock
  7737. * @apioption plotOptions.flags.useHTML
  7738. */
  7739. /**
  7740. * Fixed width of the flag's shape. By default, width is autocalculated
  7741. * according to the flag's title.
  7742. *
  7743. * @sample {highstock} stock/demo/flags-shapes/
  7744. * Flags with fixed width
  7745. *
  7746. * @type {number}
  7747. * @product highstock
  7748. * @apioption plotOptions.flags.width
  7749. */
  7750. /**
  7751. * Fixed height of the flag's shape. By default, height is
  7752. * autocalculated according to the flag's title.
  7753. *
  7754. * @type {number}
  7755. * @product highstock
  7756. * @apioption plotOptions.flags.height
  7757. */
  7758. /**
  7759. * The fill color for the flags.
  7760. *
  7761. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7762. * @product highstock
  7763. */
  7764. fillColor: palette.backgroundColor,
  7765. /**
  7766. * The color of the line/border of the flag.
  7767. *
  7768. * In styled mode, the stroke is set in the
  7769. * `.highcharts-flag-series.highcharts-point` rule.
  7770. *
  7771. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7772. * @default #000000
  7773. * @product highstock
  7774. * @apioption plotOptions.flags.lineColor
  7775. */
  7776. /**
  7777. * The pixel width of the flag's line/border.
  7778. *
  7779. * @product highstock
  7780. */
  7781. lineWidth: 1,
  7782. states: {
  7783. /**
  7784. * @extends plotOptions.column.states.hover
  7785. * @product highstock
  7786. */
  7787. hover: {
  7788. /**
  7789. * The color of the line/border of the flag.
  7790. *
  7791. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7792. * @product highstock
  7793. */
  7794. lineColor: palette.neutralColor100,
  7795. /**
  7796. * The fill or background color of the flag.
  7797. *
  7798. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7799. * @product highstock
  7800. */
  7801. fillColor: palette.highlightColor20
  7802. }
  7803. },
  7804. /**
  7805. * The text styles of the flag.
  7806. *
  7807. * In styled mode, the styles are set in the
  7808. * `.highcharts-flag-series .highcharts-point` rule.
  7809. *
  7810. * @type {Highcharts.CSSObject}
  7811. * @default {"fontSize": "11px", "fontWeight": "bold"}
  7812. * @product highstock
  7813. */
  7814. style: {
  7815. /** @ignore-option */
  7816. fontSize: '11px',
  7817. /** @ignore-option */
  7818. fontWeight: 'bold'
  7819. }
  7820. });
  7821. return FlagsSeries;
  7822. }(ColumnSeries));
  7823. extend(FlagsSeries.prototype, {
  7824. allowDG: false,
  7825. /**
  7826. * @private
  7827. * @function Highcharts.seriesTypes.flags#buildKDTree
  7828. */
  7829. buildKDTree: noop,
  7830. forceCrop: true,
  7831. getPlotBox: OnSeriesMixin.getPlotBox,
  7832. /**
  7833. * Inherit the initialization from base Series.
  7834. *
  7835. * @private
  7836. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  7837. */
  7838. init: Series.prototype.init,
  7839. /**
  7840. * Don't invert the flag marker group (#4960).
  7841. *
  7842. * @private
  7843. * @function Highcharts.seriesTypes.flags#invertGroups
  7844. */
  7845. invertGroups: noop,
  7846. // Flags series group should not be invertible (#14063).
  7847. invertible: false,
  7848. noSharedTooltip: true,
  7849. pointClass: FlagsPoint,
  7850. sorted: false,
  7851. takeOrdinalPosition: false,
  7852. trackerGroups: ['markerGroup'],
  7853. translate: OnSeriesMixin.translate
  7854. });
  7855. SeriesRegistry.registerSeriesType('flags', FlagsSeries);
  7856. /* *
  7857. *
  7858. * Default Export
  7859. *
  7860. * */
  7861. /* *
  7862. *
  7863. * API Declarations
  7864. *
  7865. * */
  7866. /**
  7867. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  7868. */
  7869. ''; // detach doclets above
  7870. /* *
  7871. *
  7872. * API Option
  7873. *
  7874. * */
  7875. /**
  7876. * A `flags` series. If the [type](#series.flags.type) option is not
  7877. * specified, it is inherited from [chart.type](#chart.type).
  7878. *
  7879. * @extends series,plotOptions.flags
  7880. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  7881. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  7882. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  7883. * pointWidth, step, turboThreshold, useOhlcData
  7884. * @product highstock
  7885. * @apioption series.flags
  7886. */
  7887. /**
  7888. * An array of data points for the series. For the `flags` series type,
  7889. * points can be given in the following ways:
  7890. *
  7891. * 1. An array of objects with named values. The following snippet shows only a
  7892. * few settings, see the complete options set below. If the total number of
  7893. * data points exceeds the series'
  7894. * [turboThreshold](#series.flags.turboThreshold), this option is not
  7895. * available.
  7896. * ```js
  7897. * data: [{
  7898. * x: 1,
  7899. * title: "A",
  7900. * text: "First event"
  7901. * }, {
  7902. * x: 1,
  7903. * title: "B",
  7904. * text: "Second event"
  7905. * }]
  7906. * ```
  7907. *
  7908. * @type {Array<*>}
  7909. * @extends series.line.data
  7910. * @excluding dataLabels, marker, name, y
  7911. * @product highstock
  7912. * @apioption series.flags.data
  7913. */
  7914. /**
  7915. * The fill color of an individual flag. By default it inherits from
  7916. * the series color.
  7917. *
  7918. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7919. * @product highstock
  7920. * @apioption series.flags.data.fillColor
  7921. */
  7922. /**
  7923. * The longer text to be shown in the flag's tooltip.
  7924. *
  7925. * @type {string}
  7926. * @product highstock
  7927. * @apioption series.flags.data.text
  7928. */
  7929. /**
  7930. * The short text to be shown on the flag.
  7931. *
  7932. * @type {string}
  7933. * @product highstock
  7934. * @apioption series.flags.data.title
  7935. */
  7936. ''; // adds doclets above to transpiled file
  7937. return FlagsSeries;
  7938. });
  7939. _registerModule(_modules, 'Extensions/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Color/Palette.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, D, palette, SVGElement, U) {
  7940. /* *
  7941. *
  7942. * (c) 2010-2021 Torstein Honsi
  7943. *
  7944. * License: www.highcharts.com/license
  7945. *
  7946. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7947. *
  7948. * */
  7949. var defaultOptions = D.defaultOptions;
  7950. var addEvent = U.addEvent,
  7951. createElement = U.createElement,
  7952. css = U.css,
  7953. defined = U.defined,
  7954. destroyObjectProperties = U.destroyObjectProperties,
  7955. discardElement = U.discardElement,
  7956. extend = U.extend,
  7957. find = U.find,
  7958. fireEvent = U.fireEvent,
  7959. isNumber = U.isNumber,
  7960. merge = U.merge,
  7961. objectEach = U.objectEach,
  7962. pad = U.pad,
  7963. pick = U.pick,
  7964. pInt = U.pInt,
  7965. splat = U.splat;
  7966. /**
  7967. * Define the time span for the button
  7968. *
  7969. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  7970. */
  7971. /**
  7972. * Callback function to react on button clicks.
  7973. *
  7974. * @callback Highcharts.RangeSelectorClickCallbackFunction
  7975. *
  7976. * @param {global.Event} e
  7977. * Event arguments.
  7978. *
  7979. * @param {boolean|undefined}
  7980. * Return false to cancel the default button event.
  7981. */
  7982. /**
  7983. * Callback function to parse values entered in the input boxes and return a
  7984. * valid JavaScript time as milliseconds since 1970.
  7985. *
  7986. * @callback Highcharts.RangeSelectorParseCallbackFunction
  7987. *
  7988. * @param {string} value
  7989. * Input value to parse.
  7990. *
  7991. * @return {number}
  7992. * Parsed JavaScript time value.
  7993. */
  7994. /* ************************************************************************** *
  7995. * Start Range Selector code *
  7996. * ************************************************************************** */
  7997. extend(defaultOptions, {
  7998. /**
  7999. * The range selector is a tool for selecting ranges to display within
  8000. * the chart. It provides buttons to select preconfigured ranges in
  8001. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  8002. * boxes where min and max dates can be manually input.
  8003. *
  8004. * @product highstock gantt
  8005. * @optionparent rangeSelector
  8006. */
  8007. rangeSelector: {
  8008. /**
  8009. * Whether to enable all buttons from the start. By default buttons are
  8010. * only enabled if the corresponding time range exists on the X axis,
  8011. * but enabling all buttons allows for dynamically loading different
  8012. * time ranges.
  8013. *
  8014. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  8015. * All buttons enabled
  8016. *
  8017. * @since 2.0.3
  8018. */
  8019. allButtonsEnabled: false,
  8020. /**
  8021. * An array of configuration objects for the buttons.
  8022. *
  8023. * Defaults to:
  8024. * ```js
  8025. * buttons: [{
  8026. * type: 'month',
  8027. * count: 1,
  8028. * text: '1m',
  8029. * title: 'View 1 month'
  8030. * }, {
  8031. * type: 'month',
  8032. * count: 3,
  8033. * text: '3m',
  8034. * title: 'View 3 months'
  8035. * }, {
  8036. * type: 'month',
  8037. * count: 6,
  8038. * text: '6m',
  8039. * title: 'View 6 months'
  8040. * }, {
  8041. * type: 'ytd',
  8042. * text: 'YTD',
  8043. * title: 'View year to date'
  8044. * }, {
  8045. * type: 'year',
  8046. * count: 1,
  8047. * text: '1y',
  8048. * title: 'View 1 year'
  8049. * }, {
  8050. * type: 'all',
  8051. * text: 'All',
  8052. * title: 'View all'
  8053. * }]
  8054. * ```
  8055. *
  8056. * @sample {highstock} stock/rangeselector/datagrouping/
  8057. * Data grouping by buttons
  8058. *
  8059. * @type {Array<*>}
  8060. */
  8061. buttons: void 0,
  8062. /**
  8063. * How many units of the defined type the button should span. If `type`
  8064. * is "month" and `count` is 3, the button spans three months.
  8065. *
  8066. * @type {number}
  8067. * @default 1
  8068. * @apioption rangeSelector.buttons.count
  8069. */
  8070. /**
  8071. * Fires when clicking on the rangeSelector button. One parameter,
  8072. * event, is passed to the function, containing common event
  8073. * information.
  8074. *
  8075. * ```js
  8076. * click: function(e) {
  8077. * console.log(this);
  8078. * }
  8079. * ```
  8080. *
  8081. * Return false to stop default button's click action.
  8082. *
  8083. * @sample {highstock} stock/rangeselector/button-click/
  8084. * Click event on the button
  8085. *
  8086. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  8087. * @apioption rangeSelector.buttons.events.click
  8088. */
  8089. /**
  8090. * Additional range (in milliseconds) added to the end of the calculated
  8091. * time span.
  8092. *
  8093. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8094. * Button offsets
  8095. *
  8096. * @type {number}
  8097. * @default 0
  8098. * @since 6.0.0
  8099. * @apioption rangeSelector.buttons.offsetMax
  8100. */
  8101. /**
  8102. * Additional range (in milliseconds) added to the start of the
  8103. * calculated time span.
  8104. *
  8105. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8106. * Button offsets
  8107. *
  8108. * @type {number}
  8109. * @default 0
  8110. * @since 6.0.0
  8111. * @apioption rangeSelector.buttons.offsetMin
  8112. */
  8113. /**
  8114. * When buttons apply dataGrouping on a series, by default zooming
  8115. * in/out will deselect buttons and unset dataGrouping. Enable this
  8116. * option to keep buttons selected when extremes change.
  8117. *
  8118. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  8119. * Different preserveDataGrouping settings
  8120. *
  8121. * @type {boolean}
  8122. * @default false
  8123. * @since 6.1.2
  8124. * @apioption rangeSelector.buttons.preserveDataGrouping
  8125. */
  8126. /**
  8127. * A custom data grouping object for each button.
  8128. *
  8129. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  8130. *
  8131. * @sample {highstock} stock/rangeselector/datagrouping/
  8132. * Data grouping by range selector buttons
  8133. *
  8134. * @type {*}
  8135. * @extends plotOptions.series.dataGrouping
  8136. * @apioption rangeSelector.buttons.dataGrouping
  8137. */
  8138. /**
  8139. * The text for the button itself.
  8140. *
  8141. * @type {string}
  8142. * @apioption rangeSelector.buttons.text
  8143. */
  8144. /**
  8145. * Explanation for the button, shown as a tooltip on hover, and used by
  8146. * assistive technology.
  8147. *
  8148. * @type {string}
  8149. * @apioption rangeSelector.buttons.title
  8150. */
  8151. /**
  8152. * Defined the time span for the button. Can be one of `millisecond`,
  8153. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  8154. * and `all`.
  8155. *
  8156. * @type {Highcharts.RangeSelectorButtonTypeValue}
  8157. * @apioption rangeSelector.buttons.type
  8158. */
  8159. /**
  8160. * The space in pixels between the buttons in the range selector.
  8161. */
  8162. buttonSpacing: 5,
  8163. /**
  8164. * Whether to collapse the range selector buttons into a dropdown when
  8165. * there is not enough room to show everything in a single row, instead
  8166. * of dividing the range selector into multiple rows.
  8167. * Can be one of the following:
  8168. * - `always`: Always collapse
  8169. * - `responsive`: Only collapse when there is not enough room
  8170. * - `never`: Never collapse
  8171. *
  8172. * @sample {highstock} stock/rangeselector/dropdown/
  8173. * Dropdown option
  8174. *
  8175. * @validvalue ["always", "responsive", "never"]
  8176. * @since 9.0.0
  8177. */
  8178. dropdown: 'responsive',
  8179. /**
  8180. * Enable or disable the range selector. Default to `true` for stock
  8181. * charts, using the `stockChart` factory.
  8182. *
  8183. * @sample {highstock} stock/rangeselector/enabled/
  8184. * Disable the range selector
  8185. *
  8186. * @type {boolean|undefined}
  8187. * @default {highstock} true
  8188. */
  8189. enabled: void 0,
  8190. /**
  8191. * The vertical alignment of the rangeselector box. Allowed properties
  8192. * are `top`, `middle`, `bottom`.
  8193. *
  8194. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  8195. * Middle
  8196. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  8197. * Bottom
  8198. *
  8199. * @type {Highcharts.VerticalAlignValue}
  8200. * @since 6.0.0
  8201. */
  8202. verticalAlign: 'top',
  8203. /**
  8204. * A collection of attributes for the buttons. The object takes SVG
  8205. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  8206. * a collection of CSS properties for the text.
  8207. *
  8208. * The object can also be extended with states, so you can set
  8209. * presentational options for `hover`, `select` or `disabled` button
  8210. * states.
  8211. *
  8212. * CSS styles for the text label.
  8213. *
  8214. * In styled mode, the buttons are styled by the
  8215. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  8216. * different states.
  8217. *
  8218. * @sample {highstock} stock/rangeselector/styling/
  8219. * Styling the buttons and inputs
  8220. *
  8221. * @type {Highcharts.SVGAttributes}
  8222. */
  8223. buttonTheme: {
  8224. /** @ignore */
  8225. width: 28,
  8226. /** @ignore */
  8227. height: 18,
  8228. /** @ignore */
  8229. padding: 2,
  8230. /** @ignore */
  8231. zIndex: 7 // #484, #852
  8232. },
  8233. /**
  8234. * When the rangeselector is floating, the plot area does not reserve
  8235. * space for it. This opens for positioning anywhere on the chart.
  8236. *
  8237. * @sample {highstock} stock/rangeselector/floating/
  8238. * Placing the range selector between the plot area and the
  8239. * navigator
  8240. *
  8241. * @since 6.0.0
  8242. */
  8243. floating: false,
  8244. /**
  8245. * The x offset of the range selector relative to its horizontal
  8246. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8247. *
  8248. * @since 6.0.0
  8249. */
  8250. x: 0,
  8251. /**
  8252. * The y offset of the range selector relative to its horizontal
  8253. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8254. *
  8255. * @since 6.0.0
  8256. */
  8257. y: 0,
  8258. /**
  8259. * Deprecated. The height of the range selector. Currently it is
  8260. * calculated dynamically.
  8261. *
  8262. * @deprecated
  8263. * @type {number|undefined}
  8264. * @since 2.1.9
  8265. */
  8266. height: void 0,
  8267. /**
  8268. * The border color of the date input boxes.
  8269. *
  8270. * @sample {highstock} stock/rangeselector/styling/
  8271. * Styling the buttons and inputs
  8272. *
  8273. * @type {Highcharts.ColorString}
  8274. * @since 1.3.7
  8275. */
  8276. inputBoxBorderColor: 'none',
  8277. /**
  8278. * The pixel height of the date input boxes.
  8279. *
  8280. * @sample {highstock} stock/rangeselector/styling/
  8281. * Styling the buttons and inputs
  8282. *
  8283. * @since 1.3.7
  8284. */
  8285. inputBoxHeight: 17,
  8286. /**
  8287. * The pixel width of the date input boxes. When `undefined`, the width
  8288. * is fitted to the rendered content.
  8289. *
  8290. * @sample {highstock} stock/rangeselector/styling/
  8291. * Styling the buttons and inputs
  8292. *
  8293. * @type {number|undefined}
  8294. * @since 1.3.7
  8295. */
  8296. inputBoxWidth: void 0,
  8297. /**
  8298. * The date format in the input boxes when not selected for editing.
  8299. * Defaults to `%b %e, %Y`.
  8300. *
  8301. * This is used to determine which type of input to show,
  8302. * `datetime-local`, `date` or `time` and falling back to `text` when
  8303. * the browser does not support the input type or the format contains
  8304. * milliseconds.
  8305. *
  8306. * @sample {highstock} stock/rangeselector/input-type/
  8307. * Input types
  8308. * @sample {highstock} stock/rangeselector/input-format/
  8309. * Milliseconds in the range selector
  8310. *
  8311. */
  8312. inputDateFormat: '%b %e, %Y',
  8313. /**
  8314. * A custom callback function to parse values entered in the input boxes
  8315. * and return a valid JavaScript time as milliseconds since 1970.
  8316. * The first argument passed is a value to parse,
  8317. * second is a boolean indicating use of the UTC time.
  8318. *
  8319. * This will only get called for inputs of type `text`. Since v8.2.3,
  8320. * the input type is dynamically determined based on the granularity
  8321. * of the `inputDateFormat` and the browser support.
  8322. *
  8323. * @sample {highstock} stock/rangeselector/input-format/
  8324. * Milliseconds in the range selector
  8325. *
  8326. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  8327. * @since 1.3.3
  8328. */
  8329. inputDateParser: void 0,
  8330. /**
  8331. * The date format in the input boxes when they are selected for
  8332. * editing. This must be a format that is recognized by JavaScript
  8333. * Date.parse.
  8334. *
  8335. * This will only be used for inputs of type `text`. Since v8.2.3,
  8336. * the input type is dynamically determined based on the granularity
  8337. * of the `inputDateFormat` and the browser support.
  8338. *
  8339. * @sample {highstock} stock/rangeselector/input-format/
  8340. * Milliseconds in the range selector
  8341. *
  8342. */
  8343. inputEditDateFormat: '%Y-%m-%d',
  8344. /**
  8345. * Enable or disable the date input boxes.
  8346. */
  8347. inputEnabled: true,
  8348. /**
  8349. * Positioning for the input boxes. Allowed properties are `align`,
  8350. * `x` and `y`.
  8351. *
  8352. * @since 1.2.4
  8353. */
  8354. inputPosition: {
  8355. /**
  8356. * The alignment of the input box. Allowed properties are `left`,
  8357. * `center`, `right`.
  8358. *
  8359. * @sample {highstock} stock/rangeselector/input-button-position/
  8360. * Alignment
  8361. *
  8362. * @type {Highcharts.AlignValue}
  8363. * @since 6.0.0
  8364. */
  8365. align: 'right',
  8366. /**
  8367. * X offset of the input row.
  8368. */
  8369. x: 0,
  8370. /**
  8371. * Y offset of the input row.
  8372. */
  8373. y: 0
  8374. },
  8375. /**
  8376. * The space in pixels between the labels and the date input boxes in
  8377. * the range selector.
  8378. *
  8379. * @since 9.0.0
  8380. */
  8381. inputSpacing: 5,
  8382. /**
  8383. * The index of the button to appear pre-selected.
  8384. *
  8385. * @type {number}
  8386. */
  8387. selected: void 0,
  8388. /**
  8389. * Positioning for the button row.
  8390. *
  8391. * @since 1.2.4
  8392. */
  8393. buttonPosition: {
  8394. /**
  8395. * The alignment of the input box. Allowed properties are `left`,
  8396. * `center`, `right`.
  8397. *
  8398. * @sample {highstock} stock/rangeselector/input-button-position/
  8399. * Alignment
  8400. *
  8401. * @type {Highcharts.AlignValue}
  8402. * @since 6.0.0
  8403. */
  8404. align: 'left',
  8405. /**
  8406. * X offset of the button row.
  8407. */
  8408. x: 0,
  8409. /**
  8410. * Y offset of the button row.
  8411. */
  8412. y: 0
  8413. },
  8414. /**
  8415. * CSS for the HTML inputs in the range selector.
  8416. *
  8417. * In styled mode, the inputs are styled by the
  8418. * `.highcharts-range-input text` rule in SVG mode, and
  8419. * `input.highcharts-range-selector` when active.
  8420. *
  8421. * @sample {highstock} stock/rangeselector/styling/
  8422. * Styling the buttons and inputs
  8423. *
  8424. * @type {Highcharts.CSSObject}
  8425. * @apioption rangeSelector.inputStyle
  8426. */
  8427. inputStyle: {
  8428. /** @ignore */
  8429. color: palette.highlightColor80,
  8430. /** @ignore */
  8431. cursor: 'pointer'
  8432. },
  8433. /**
  8434. * CSS styles for the labels - the Zoom, From and To texts.
  8435. *
  8436. * In styled mode, the labels are styled by the
  8437. * `.highcharts-range-label` class.
  8438. *
  8439. * @sample {highstock} stock/rangeselector/styling/
  8440. * Styling the buttons and inputs
  8441. *
  8442. * @type {Highcharts.CSSObject}
  8443. */
  8444. labelStyle: {
  8445. /** @ignore */
  8446. color: palette.neutralColor60
  8447. }
  8448. }
  8449. });
  8450. extend(defaultOptions.lang,
  8451. /**
  8452. * Language object. The language object is global and it can't be set
  8453. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  8454. * set it before any chart is initialized.
  8455. *
  8456. * ```js
  8457. * Highcharts.setOptions({
  8458. * lang: {
  8459. * months: [
  8460. * 'Janvier', 'Février', 'Mars', 'Avril',
  8461. * 'Mai', 'Juin', 'Juillet', 'Août',
  8462. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  8463. * ],
  8464. * weekdays: [
  8465. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  8466. * 'Jeudi', 'Vendredi', 'Samedi'
  8467. * ]
  8468. * }
  8469. * });
  8470. * ```
  8471. *
  8472. * @optionparent lang
  8473. */
  8474. {
  8475. /**
  8476. * The text for the label for the range selector buttons.
  8477. *
  8478. * @product highstock gantt
  8479. */
  8480. rangeSelectorZoom: 'Zoom',
  8481. /**
  8482. * The text for the label for the "from" input box in the range
  8483. * selector. Since v9.0, this string is empty as the label is not
  8484. * rendered by default.
  8485. *
  8486. * @product highstock gantt
  8487. */
  8488. rangeSelectorFrom: '',
  8489. /**
  8490. * The text for the label for the "to" input box in the range selector.
  8491. *
  8492. * @product highstock gantt
  8493. */
  8494. rangeSelectorTo: '→'
  8495. });
  8496. /* eslint-disable no-invalid-this, valid-jsdoc */
  8497. /**
  8498. * The range selector.
  8499. *
  8500. * @private
  8501. * @class
  8502. * @name Highcharts.RangeSelector
  8503. * @param {Highcharts.Chart} chart
  8504. */
  8505. var RangeSelector = /** @class */ (function () {
  8506. function RangeSelector(chart) {
  8507. /* *
  8508. *
  8509. * Properties
  8510. *
  8511. * */
  8512. this.buttons = void 0;
  8513. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  8514. this.initialButtonGroupWidth = 0;
  8515. this.options = void 0;
  8516. this.chart = chart;
  8517. // Run RangeSelector
  8518. this.init(chart);
  8519. }
  8520. /**
  8521. * The method to run when one of the buttons in the range selectors is
  8522. * clicked
  8523. *
  8524. * @private
  8525. * @function Highcharts.RangeSelector#clickButton
  8526. * @param {number} i
  8527. * The index of the button
  8528. * @param {boolean} [redraw]
  8529. * @return {void}
  8530. */
  8531. RangeSelector.prototype.clickButton = function (i, redraw) {
  8532. var rangeSelector = this,
  8533. chart = rangeSelector.chart,
  8534. rangeOptions = rangeSelector.buttonOptions[i],
  8535. baseAxis = chart.xAxis[0],
  8536. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  8537. dataMin = unionExtremes.dataMin,
  8538. dataMax = unionExtremes.dataMax,
  8539. newMin,
  8540. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  8541. pick(dataMax,
  8542. baseAxis.max))), // #1568
  8543. type = rangeOptions.type,
  8544. baseXAxisOptions,
  8545. range = rangeOptions._range,
  8546. rangeMin,
  8547. minSetting,
  8548. rangeSetting,
  8549. ctx,
  8550. ytdExtremes,
  8551. dataGrouping = rangeOptions.dataGrouping;
  8552. // chart has no data, base series is removed
  8553. if (dataMin === null || dataMax === null) {
  8554. return;
  8555. }
  8556. // Set the fixed range before range is altered
  8557. chart.fixedRange = range;
  8558. rangeSelector.setSelected(i);
  8559. // Apply dataGrouping associated to button
  8560. if (dataGrouping) {
  8561. this.forcedDataGrouping = true;
  8562. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  8563. this.frozenStates = rangeOptions.preserveDataGrouping;
  8564. }
  8565. // Apply range
  8566. if (type === 'month' || type === 'year') {
  8567. if (!baseAxis) {
  8568. // This is set to the user options and picked up later when the
  8569. // axis is instantiated so that we know the min and max.
  8570. range = rangeOptions;
  8571. }
  8572. else {
  8573. ctx = {
  8574. range: rangeOptions,
  8575. max: newMax,
  8576. chart: chart,
  8577. dataMin: dataMin,
  8578. dataMax: dataMax
  8579. };
  8580. newMin = baseAxis.minFromRange.call(ctx);
  8581. if (isNumber(ctx.newMax)) {
  8582. newMax = ctx.newMax;
  8583. }
  8584. }
  8585. // Fixed times like minutes, hours, days
  8586. }
  8587. else if (range) {
  8588. newMin = Math.max(newMax - range, dataMin);
  8589. newMax = Math.min(newMin + range, dataMax);
  8590. }
  8591. else if (type === 'ytd') {
  8592. // On user clicks on the buttons, or a delayed action running from
  8593. // the beforeRender event (below), the baseAxis is defined.
  8594. if (baseAxis) {
  8595. // When "ytd" is the pre-selected button for the initial view,
  8596. // its calculation is delayed and rerun in the beforeRender
  8597. // event (below). When the series are initialized, but before
  8598. // the chart is rendered, we have access to the xData array
  8599. // (#942).
  8600. if (typeof dataMax === 'undefined') {
  8601. dataMin = Number.MAX_VALUE;
  8602. dataMax = Number.MIN_VALUE;
  8603. chart.series.forEach(function (series) {
  8604. // reassign it to the last item
  8605. var xData = series.xData;
  8606. dataMin = Math.min(xData[0], dataMin);
  8607. dataMax = Math.max(xData[xData.length - 1], dataMax);
  8608. });
  8609. redraw = false;
  8610. }
  8611. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  8612. newMin = rangeMin = ytdExtremes.min;
  8613. newMax = ytdExtremes.max;
  8614. // "ytd" is pre-selected. We don't yet have access to processed
  8615. // point and extremes data (things like pointStart and pointInterval
  8616. // are missing), so we delay the process (#942)
  8617. }
  8618. else {
  8619. rangeSelector.deferredYTDClick = i;
  8620. return;
  8621. }
  8622. }
  8623. else if (type === 'all' && baseAxis) {
  8624. // If the navigator exist and the axis range is declared reset that
  8625. // range and from now on only use the range set by a user, #14742.
  8626. if (chart.navigator && chart.navigator.baseSeries[0]) {
  8627. chart.navigator.baseSeries[0].xAxis.options.range = void 0;
  8628. }
  8629. newMin = dataMin;
  8630. newMax = dataMax;
  8631. }
  8632. if (defined(newMin)) {
  8633. newMin += rangeOptions._offsetMin;
  8634. }
  8635. if (defined(newMax)) {
  8636. newMax += rangeOptions._offsetMax;
  8637. }
  8638. if (this.dropdown) {
  8639. this.dropdown.selectedIndex = i + 1;
  8640. }
  8641. // Update the chart
  8642. if (!baseAxis) {
  8643. // Axis not yet instanciated. Temporarily set min and range
  8644. // options and remove them on chart load (#4317).
  8645. baseXAxisOptions = splat(chart.options.xAxis)[0];
  8646. rangeSetting = baseXAxisOptions.range;
  8647. baseXAxisOptions.range = range;
  8648. minSetting = baseXAxisOptions.min;
  8649. baseXAxisOptions.min = rangeMin;
  8650. addEvent(chart, 'load', function resetMinAndRange() {
  8651. baseXAxisOptions.range = rangeSetting;
  8652. baseXAxisOptions.min = minSetting;
  8653. });
  8654. }
  8655. else {
  8656. // Existing axis object. Set extremes after render time.
  8657. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  8658. {
  8659. trigger: 'rangeSelectorButton',
  8660. rangeSelectorButton: rangeOptions
  8661. });
  8662. }
  8663. fireEvent(this, 'afterBtnClick');
  8664. };
  8665. /**
  8666. * Set the selected option. This method only sets the internal flag, it
  8667. * doesn't update the buttons or the actual zoomed range.
  8668. *
  8669. * @private
  8670. * @function Highcharts.RangeSelector#setSelected
  8671. * @param {number} [selected]
  8672. * @return {void}
  8673. */
  8674. RangeSelector.prototype.setSelected = function (selected) {
  8675. this.selected = this.options.selected = selected;
  8676. };
  8677. /**
  8678. * Initialize the range selector
  8679. *
  8680. * @private
  8681. * @function Highcharts.RangeSelector#init
  8682. * @param {Highcharts.Chart} chart
  8683. * @return {void}
  8684. */
  8685. RangeSelector.prototype.init = function (chart) {
  8686. var rangeSelector = this,
  8687. options = chart.options.rangeSelector,
  8688. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  8689. selectedOption = options.selected,
  8690. blurInputs = function () {
  8691. var minInput = rangeSelector.minInput,
  8692. maxInput = rangeSelector.maxInput;
  8693. // #3274 in some case blur is not defined
  8694. if (minInput && minInput.blur) {
  8695. fireEvent(minInput, 'blur');
  8696. }
  8697. if (maxInput && maxInput.blur) {
  8698. fireEvent(maxInput, 'blur');
  8699. }
  8700. };
  8701. rangeSelector.chart = chart;
  8702. rangeSelector.options = options;
  8703. rangeSelector.buttons = [];
  8704. rangeSelector.buttonOptions = buttonOptions;
  8705. this.eventsToUnbind = [];
  8706. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  8707. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  8708. // Extend the buttonOptions with actual range
  8709. buttonOptions.forEach(rangeSelector.computeButtonRange);
  8710. // zoomed range based on a pre-selected button index
  8711. if (typeof selectedOption !== 'undefined' &&
  8712. buttonOptions[selectedOption]) {
  8713. this.clickButton(selectedOption, false);
  8714. }
  8715. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  8716. // If a data grouping is applied to the current button, release it
  8717. // when extremes change
  8718. if (chart.xAxis && chart.xAxis[0]) {
  8719. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  8720. if (this.max - this.min !==
  8721. chart.fixedRange &&
  8722. e.trigger !== 'rangeSelectorButton' &&
  8723. e.trigger !== 'updatedData' &&
  8724. rangeSelector.forcedDataGrouping &&
  8725. !rangeSelector.frozenStates) {
  8726. this.setDataGrouping(false, false);
  8727. }
  8728. });
  8729. }
  8730. }));
  8731. };
  8732. /**
  8733. * Dynamically update the range selector buttons after a new range has been
  8734. * set
  8735. *
  8736. * @private
  8737. * @function Highcharts.RangeSelector#updateButtonStates
  8738. * @return {void}
  8739. */
  8740. RangeSelector.prototype.updateButtonStates = function () {
  8741. var rangeSelector = this,
  8742. chart = this.chart,
  8743. dropdown = this.dropdown,
  8744. baseAxis = chart.xAxis[0],
  8745. actualRange = Math.round(baseAxis.max - baseAxis.min),
  8746. hasNoData = !baseAxis.hasVisibleSeries,
  8747. day = 24 * 36e5, // A single day in milliseconds
  8748. unionExtremes = (chart.scroller &&
  8749. chart.scroller.getUnionExtremes()) || baseAxis,
  8750. dataMin = unionExtremes.dataMin,
  8751. dataMax = unionExtremes.dataMax,
  8752. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  8753. dataMin,
  8754. chart.time.useUTC),
  8755. ytdMin = ytdExtremes.min,
  8756. ytdMax = ytdExtremes.max,
  8757. selected = rangeSelector.selected,
  8758. selectedExists = isNumber(selected),
  8759. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  8760. buttons = rangeSelector.buttons;
  8761. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  8762. var range = rangeOptions._range,
  8763. type = rangeOptions.type,
  8764. count = rangeOptions.count || 1,
  8765. button = buttons[i],
  8766. state = 0,
  8767. disable,
  8768. select,
  8769. offsetRange = rangeOptions._offsetMax -
  8770. rangeOptions._offsetMin,
  8771. isSelected = i === selected,
  8772. // Disable buttons where the range exceeds what is allowed in
  8773. // the current view
  8774. isTooGreatRange = range >
  8775. dataMax - dataMin,
  8776. // Disable buttons where the range is smaller than the minimum
  8777. // range
  8778. isTooSmallRange = range < baseAxis.minRange,
  8779. // Do not select the YTD button if not explicitly told so
  8780. isYTDButNotSelected = false,
  8781. // Disable the All button if we're already showing all
  8782. isAllButAlreadyShowingAll = false,
  8783. isSameRange = range === actualRange;
  8784. // Months and years have a variable range so we check the extremes
  8785. if ((type === 'month' || type === 'year') &&
  8786. (actualRange + 36e5 >=
  8787. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  8788. (actualRange - 36e5 <=
  8789. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  8790. isSameRange = true;
  8791. }
  8792. else if (type === 'ytd') {
  8793. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  8794. isYTDButNotSelected = !isSelected;
  8795. }
  8796. else if (type === 'all') {
  8797. isSameRange = (baseAxis.max - baseAxis.min >=
  8798. dataMax - dataMin);
  8799. isAllButAlreadyShowingAll = (!isSelected &&
  8800. selectedExists &&
  8801. isSameRange);
  8802. }
  8803. // The new zoom area happens to match the range for a button - mark
  8804. // it selected. This happens when scrolling across an ordinal gap.
  8805. // It can be seen in the intraday demos when selecting 1h and scroll
  8806. // across the night gap.
  8807. disable = (!allButtonsEnabled &&
  8808. (isTooGreatRange ||
  8809. isTooSmallRange ||
  8810. isAllButAlreadyShowingAll ||
  8811. hasNoData));
  8812. select = ((isSelected && isSameRange) ||
  8813. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  8814. (isSelected && rangeSelector.frozenStates));
  8815. if (disable) {
  8816. state = 3;
  8817. }
  8818. else if (select) {
  8819. selectedExists = true; // Only one button can be selected
  8820. state = 2;
  8821. }
  8822. // If state has changed, update the button
  8823. if (button.state !== state) {
  8824. button.setState(state);
  8825. if (dropdown) {
  8826. dropdown.options[i + 1].disabled = disable;
  8827. if (state === 2) {
  8828. dropdown.selectedIndex = i + 1;
  8829. }
  8830. }
  8831. // Reset (#9209)
  8832. if (state === 0 && selected === i) {
  8833. rangeSelector.setSelected();
  8834. }
  8835. }
  8836. });
  8837. };
  8838. /**
  8839. * Compute and cache the range for an individual button
  8840. *
  8841. * @private
  8842. * @function Highcharts.RangeSelector#computeButtonRange
  8843. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  8844. * @return {void}
  8845. */
  8846. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  8847. var type = rangeOptions.type,
  8848. count = rangeOptions.count || 1,
  8849. // these time intervals have a fixed number of milliseconds, as
  8850. // opposed to month, ytd and year
  8851. fixedTimes = {
  8852. millisecond: 1,
  8853. second: 1000,
  8854. minute: 60 * 1000,
  8855. hour: 3600 * 1000,
  8856. day: 24 * 3600 * 1000,
  8857. week: 7 * 24 * 3600 * 1000
  8858. };
  8859. // Store the range on the button object
  8860. if (fixedTimes[type]) {
  8861. rangeOptions._range = fixedTimes[type] * count;
  8862. }
  8863. else if (type === 'month' || type === 'year') {
  8864. rangeOptions._range = {
  8865. month: 30,
  8866. year: 365
  8867. }[type] * 24 * 36e5 * count;
  8868. }
  8869. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  8870. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  8871. rangeOptions._range +=
  8872. rangeOptions._offsetMax - rangeOptions._offsetMin;
  8873. };
  8874. /**
  8875. * Get the unix timestamp of a HTML input for the dates
  8876. *
  8877. * @private
  8878. * @function Highcharts.RangeSelector#getInputValue
  8879. * @param {string} name
  8880. * @return {number}
  8881. */
  8882. RangeSelector.prototype.getInputValue = function (name) {
  8883. var input = name === 'min' ? this.minInput : this.maxInput;
  8884. var options = this.chart.options.rangeSelector;
  8885. var time = this.chart.time;
  8886. if (input) {
  8887. return ((input.type === 'text' && options.inputDateParser) ||
  8888. this.defaultInputDateParser)(input.value, time.useUTC, time);
  8889. }
  8890. return 0;
  8891. };
  8892. /**
  8893. * Set the internal and displayed value of a HTML input for the dates
  8894. *
  8895. * @private
  8896. * @function Highcharts.RangeSelector#setInputValue
  8897. * @param {string} name
  8898. * @param {number} [inputTime]
  8899. * @return {void}
  8900. */
  8901. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  8902. var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8903. if (input) {
  8904. var hcTimeAttr = input.getAttribute('data-hc-time');
  8905. var updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  8906. if (defined(inputTime)) {
  8907. var previousTime = updatedTime;
  8908. if (defined(previousTime)) {
  8909. input.setAttribute('data-hc-time-previous', previousTime);
  8910. }
  8911. input.setAttribute('data-hc-time', inputTime);
  8912. updatedTime = inputTime;
  8913. }
  8914. input.value = time.dateFormat(this.inputTypeFormats[input.type] || options.inputEditDateFormat, updatedTime);
  8915. if (dateBox) {
  8916. dateBox.attr({
  8917. text: time.dateFormat(options.inputDateFormat, updatedTime)
  8918. });
  8919. }
  8920. }
  8921. };
  8922. /**
  8923. * Set the min and max value of a HTML input for the dates
  8924. *
  8925. * @private
  8926. * @function Highcharts.RangeSelector#setInputExtremes
  8927. * @param {string} name
  8928. * @param {number} min
  8929. * @param {number} max
  8930. * @return {void}
  8931. */
  8932. RangeSelector.prototype.setInputExtremes = function (name, min, max) {
  8933. var input = name === 'min' ? this.minInput : this.maxInput;
  8934. if (input) {
  8935. var format = this.inputTypeFormats[input.type];
  8936. var time = this.chart.time;
  8937. if (format) {
  8938. var newMin = time.dateFormat(format,
  8939. min);
  8940. if (input.min !== newMin) {
  8941. input.min = newMin;
  8942. }
  8943. var newMax = time.dateFormat(format,
  8944. max);
  8945. if (input.max !== newMax) {
  8946. input.max = newMax;
  8947. }
  8948. }
  8949. }
  8950. };
  8951. /**
  8952. * @private
  8953. * @function Highcharts.RangeSelector#showInput
  8954. * @param {string} name
  8955. * @return {void}
  8956. */
  8957. RangeSelector.prototype.showInput = function (name) {
  8958. var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8959. var input = name === 'min' ? this.minInput : this.maxInput;
  8960. if (input && dateBox && this.inputGroup) {
  8961. var isTextInput = input.type === 'text';
  8962. var _a = this.inputGroup,
  8963. translateX = _a.translateX,
  8964. translateY = _a.translateY;
  8965. var inputBoxWidth = this.options.inputBoxWidth;
  8966. css(input, {
  8967. width: isTextInput ? ((dateBox.width + (inputBoxWidth ? -2 : 20)) + 'px') : 'auto',
  8968. height: isTextInput ? ((dateBox.height - 2) + 'px') : 'auto',
  8969. border: '2px solid silver'
  8970. });
  8971. if (isTextInput && inputBoxWidth) {
  8972. css(input, {
  8973. left: (translateX + dateBox.x) + 'px',
  8974. top: translateY + 'px'
  8975. });
  8976. // Inputs of types date, time or datetime-local should be centered
  8977. // on top of the dateBox
  8978. }
  8979. else {
  8980. css(input, {
  8981. left: Math.min(Math.round(dateBox.x +
  8982. translateX -
  8983. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  8984. top: (translateY - (input.offsetHeight - dateBox.height) / 2) + 'px'
  8985. });
  8986. }
  8987. }
  8988. };
  8989. /**
  8990. * @private
  8991. * @function Highcharts.RangeSelector#hideInput
  8992. * @param {string} name
  8993. * @return {void}
  8994. */
  8995. RangeSelector.prototype.hideInput = function (name) {
  8996. var input = name === 'min' ? this.minInput : this.maxInput;
  8997. if (input) {
  8998. css(input, {
  8999. top: '-9999em',
  9000. border: 0,
  9001. width: '1px',
  9002. height: '1px'
  9003. });
  9004. }
  9005. };
  9006. /**
  9007. * @private
  9008. * @function Highcharts.RangeSelector#defaultInputDateParser
  9009. */
  9010. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
  9011. var hasTimezone = function (str) {
  9012. return str.length > 6 &&
  9013. (str.lastIndexOf('-') === str.length - 6 ||
  9014. str.lastIndexOf('+') === str.length - 6);
  9015. };
  9016. var input = inputDate.split('/').join('-').split(' ').join('T');
  9017. if (input.indexOf('T') === -1) {
  9018. input += 'T00:00';
  9019. }
  9020. if (useUTC) {
  9021. input += 'Z';
  9022. }
  9023. else if (H.isSafari && !hasTimezone(input)) {
  9024. var offset = new Date(input).getTimezoneOffset() / 60;
  9025. input += offset <= 0 ? "+" + pad(-offset) + ":00" : "-" + pad(offset) + ":00";
  9026. }
  9027. var date = Date.parse(input);
  9028. // If the value isn't parsed directly to a value by the
  9029. // browser's Date.parse method, like YYYY-MM-DD in IE8, try
  9030. // parsing it a different way
  9031. if (!isNumber(date)) {
  9032. var parts = inputDate.split('-');
  9033. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  9034. }
  9035. if (time && useUTC && isNumber(date)) {
  9036. date += time.getTimezoneOffset(date);
  9037. }
  9038. return date;
  9039. };
  9040. /**
  9041. * Draw either the 'from' or the 'to' HTML input box of the range selector
  9042. *
  9043. * @private
  9044. * @function Highcharts.RangeSelector#drawInput
  9045. * @param {string} name
  9046. * @return {RangeSelectorInputElements}
  9047. */
  9048. RangeSelector.prototype.drawInput = function (name) {
  9049. var _a = this,
  9050. chart = _a.chart,
  9051. div = _a.div,
  9052. inputGroup = _a.inputGroup;
  9053. var rangeSelector = this,
  9054. chartStyle = chart.renderer.style || {},
  9055. renderer = chart.renderer,
  9056. options = chart.options.rangeSelector,
  9057. lang = defaultOptions.lang,
  9058. isMin = name === 'min';
  9059. /**
  9060. * @private
  9061. */
  9062. function updateExtremes() {
  9063. var value = rangeSelector.getInputValue(name),
  9064. chartAxis = chart.xAxis[0],
  9065. dataAxis = chart.scroller && chart.scroller.xAxis ?
  9066. chart.scroller.xAxis :
  9067. chartAxis,
  9068. dataMin = dataAxis.dataMin,
  9069. dataMax = dataAxis.dataMax;
  9070. var maxInput = rangeSelector.maxInput,
  9071. minInput = rangeSelector.minInput;
  9072. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  9073. isNumber(value)) {
  9074. input.setAttribute('data-hc-time-previous', value);
  9075. // Validate the extremes. If it goes beyound the data min or
  9076. // max, use the actual data extreme (#2438).
  9077. if (isMin && maxInput && isNumber(dataMin)) {
  9078. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  9079. value = void 0;
  9080. }
  9081. else if (value < dataMin) {
  9082. value = dataMin;
  9083. }
  9084. }
  9085. else if (minInput && isNumber(dataMax)) {
  9086. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  9087. value = void 0;
  9088. }
  9089. else if (value > dataMax) {
  9090. value = dataMax;
  9091. }
  9092. }
  9093. // Set the extremes
  9094. if (typeof value !== 'undefined') { // @todo typof undefined
  9095. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  9096. }
  9097. }
  9098. }
  9099. // Create the text label
  9100. var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'];
  9101. var label = renderer
  9102. .label(text, 0)
  9103. .addClass('highcharts-range-label')
  9104. .attr({
  9105. padding: text ? 2 : 0,
  9106. height: text ? options.inputBoxHeight : 0
  9107. })
  9108. .add(inputGroup);
  9109. // Create an SVG label that shows updated date ranges and and records
  9110. // click events that bring in the HTML input.
  9111. var dateBox = renderer
  9112. .label('', 0)
  9113. .addClass('highcharts-range-input')
  9114. .attr({
  9115. padding: 2,
  9116. width: options.inputBoxWidth,
  9117. height: options.inputBoxHeight,
  9118. 'text-align': 'center'
  9119. })
  9120. .on('click',
  9121. function () {
  9122. // If it is already focused, the onfocus event doesn't fire
  9123. // (#3713)
  9124. rangeSelector.showInput(name);
  9125. rangeSelector[name + 'Input'].focus();
  9126. });
  9127. if (!chart.styledMode) {
  9128. dateBox.attr({
  9129. stroke: options.inputBoxBorderColor,
  9130. 'stroke-width': 1
  9131. });
  9132. }
  9133. dateBox.add(inputGroup);
  9134. // Create the HTML input element. This is rendered as 1x1 pixel then set
  9135. // to the right size when focused.
  9136. var input = createElement('input', {
  9137. name: name,
  9138. className: 'highcharts-range-selector'
  9139. },
  9140. void 0,
  9141. div);
  9142. // #14788: Setting input.type to an unsupported type throws in IE, so
  9143. // we need to use setAttribute instead
  9144. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%b %e, %Y'));
  9145. if (!chart.styledMode) {
  9146. // Styles
  9147. label.css(merge(chartStyle, options.labelStyle));
  9148. dateBox.css(merge({
  9149. color: palette.neutralColor80
  9150. }, chartStyle, options.inputStyle));
  9151. css(input, extend({
  9152. position: 'absolute',
  9153. border: 0,
  9154. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  9155. width: '1px',
  9156. height: '1px',
  9157. padding: 0,
  9158. textAlign: 'center',
  9159. fontSize: chartStyle.fontSize,
  9160. fontFamily: chartStyle.fontFamily,
  9161. top: '-9999em' // #4798
  9162. }, options.inputStyle));
  9163. }
  9164. // Blow up the input box
  9165. input.onfocus = function () {
  9166. rangeSelector.showInput(name);
  9167. };
  9168. // Hide away the input box
  9169. input.onblur = function () {
  9170. // update extermes only when inputs are active
  9171. if (input === H.doc.activeElement) { // Only when focused
  9172. // Update also when no `change` event is triggered, like when
  9173. // clicking inside the SVG (#4710)
  9174. updateExtremes();
  9175. }
  9176. // #10404 - move hide and blur outside focus
  9177. rangeSelector.hideInput(name);
  9178. rangeSelector.setInputValue(name);
  9179. input.blur(); // #4606
  9180. };
  9181. var keyDown = false;
  9182. // handle changes in the input boxes
  9183. input.onchange = function () {
  9184. // Update extremes and blur input when clicking date input calendar
  9185. if (!keyDown) {
  9186. updateExtremes();
  9187. rangeSelector.hideInput(name);
  9188. input.blur();
  9189. }
  9190. };
  9191. input.onkeypress = function (event) {
  9192. // IE does not fire onchange on enter
  9193. if (event.keyCode === 13) {
  9194. updateExtremes();
  9195. }
  9196. };
  9197. input.onkeydown = function (event) {
  9198. keyDown = true;
  9199. // Arrow keys
  9200. if (event.keyCode === 38 || event.keyCode === 40) {
  9201. updateExtremes();
  9202. }
  9203. };
  9204. input.onkeyup = function () {
  9205. keyDown = false;
  9206. };
  9207. return { dateBox: dateBox, input: input, label: label };
  9208. };
  9209. /**
  9210. * Get the position of the range selector buttons and inputs. This can be
  9211. * overridden from outside for custom positioning.
  9212. *
  9213. * @private
  9214. * @function Highcharts.RangeSelector#getPosition
  9215. *
  9216. * @return {Highcharts.Dictionary<number>}
  9217. */
  9218. RangeSelector.prototype.getPosition = function () {
  9219. var chart = this.chart,
  9220. options = chart.options.rangeSelector,
  9221. top = options.verticalAlign === 'top' ?
  9222. chart.plotTop - chart.axisOffset[0] :
  9223. 0; // set offset only for varticalAlign top
  9224. return {
  9225. buttonTop: top + options.buttonPosition.y,
  9226. inputTop: top + options.inputPosition.y - 10
  9227. };
  9228. };
  9229. /**
  9230. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  9231. * the current timestamp. Will choose dataMin if its value is higher than
  9232. * the timestamp for the start of current year.
  9233. *
  9234. * @private
  9235. * @function Highcharts.RangeSelector#getYTDExtremes
  9236. *
  9237. * @param {number} dataMax
  9238. *
  9239. * @param {number} dataMin
  9240. *
  9241. * @return {*}
  9242. * Returns min and max for the YTD
  9243. */
  9244. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  9245. var time = this.chart.time,
  9246. min,
  9247. now = new time.Date(dataMax),
  9248. year = time.get('FullYear',
  9249. now),
  9250. startOfYear = useUTC ?
  9251. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  9252. +new time.Date(year, 0, 1);
  9253. min = Math.max(dataMin, startOfYear);
  9254. var ts = now.getTime();
  9255. return {
  9256. max: Math.min(dataMax || ts, ts),
  9257. min: min
  9258. };
  9259. };
  9260. /**
  9261. * Render the range selector including the buttons and the inputs. The first
  9262. * time render is called, the elements are created and positioned. On
  9263. * subsequent calls, they are moved and updated.
  9264. *
  9265. * @private
  9266. * @function Highcharts.RangeSelector#render
  9267. * @param {number} [min]
  9268. * X axis minimum
  9269. * @param {number} [max]
  9270. * X axis maximum
  9271. * @return {void}
  9272. */
  9273. RangeSelector.prototype.render = function (min, max) {
  9274. var chart = this.chart,
  9275. renderer = chart.renderer,
  9276. container = chart.container,
  9277. chartOptions = chart.options,
  9278. options = chartOptions.rangeSelector,
  9279. // Place inputs above the container
  9280. inputsZIndex = pick(chartOptions.chart.style &&
  9281. chartOptions.chart.style.zIndex, 0) + 1,
  9282. inputEnabled = options.inputEnabled,
  9283. rendered = this.rendered;
  9284. if (options.enabled === false) {
  9285. return;
  9286. }
  9287. // create the elements
  9288. if (!rendered) {
  9289. this.group = renderer.g('range-selector-group')
  9290. .attr({
  9291. zIndex: 7
  9292. })
  9293. .add();
  9294. this.div = createElement('div', void 0, {
  9295. position: 'relative',
  9296. height: 0,
  9297. zIndex: inputsZIndex
  9298. });
  9299. if (this.buttonOptions.length) {
  9300. this.renderButtons();
  9301. }
  9302. // First create a wrapper outside the container in order to make
  9303. // the inputs work and make export correct
  9304. if (container.parentNode) {
  9305. container.parentNode.insertBefore(this.div, container);
  9306. }
  9307. if (inputEnabled) {
  9308. // Create the group to keep the inputs
  9309. this.inputGroup = renderer.g('input-group').add(this.group);
  9310. var minElems = this.drawInput('min');
  9311. this.minDateBox = minElems.dateBox;
  9312. this.minLabel = minElems.label;
  9313. this.minInput = minElems.input;
  9314. var maxElems = this.drawInput('max');
  9315. this.maxDateBox = maxElems.dateBox;
  9316. this.maxLabel = maxElems.label;
  9317. this.maxInput = maxElems.input;
  9318. }
  9319. }
  9320. if (inputEnabled) {
  9321. // Set or reset the input values
  9322. this.setInputValue('min', min);
  9323. this.setInputValue('max', max);
  9324. var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  9325. if (defined(unionExtremes.dataMin) && defined(unionExtremes.dataMax)) {
  9326. var minRange = chart.xAxis[0].minRange || 0;
  9327. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  9328. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  9329. }
  9330. // Reflow
  9331. if (this.inputGroup) {
  9332. var x_1 = 0;
  9333. [
  9334. this.minLabel,
  9335. this.minDateBox,
  9336. this.maxLabel,
  9337. this.maxDateBox
  9338. ].forEach(function (label) {
  9339. if (label) {
  9340. var width = label.getBBox().width;
  9341. if (width) {
  9342. label.attr({ x: x_1 });
  9343. x_1 += width + options.inputSpacing;
  9344. }
  9345. }
  9346. });
  9347. }
  9348. }
  9349. this.alignElements();
  9350. this.rendered = true;
  9351. };
  9352. /**
  9353. * Render the range buttons. This only runs the first time, later the
  9354. * positioning is laid out in alignElements.
  9355. *
  9356. * @private
  9357. * @function Highcharts.RangeSelector#renderButtons
  9358. * @return {void}
  9359. */
  9360. RangeSelector.prototype.renderButtons = function () {
  9361. var _this = this;
  9362. var _a = this,
  9363. buttons = _a.buttons,
  9364. chart = _a.chart,
  9365. options = _a.options;
  9366. var lang = defaultOptions.lang;
  9367. var renderer = chart.renderer;
  9368. var buttonTheme = merge(options.buttonTheme);
  9369. var states = buttonTheme && buttonTheme.states;
  9370. // Prevent the button from resetting the width when the button state
  9371. // changes since we need more control over the width when collapsing
  9372. // the buttons
  9373. var width = buttonTheme.width || 28;
  9374. delete buttonTheme.width;
  9375. delete buttonTheme.states;
  9376. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  9377. var dropdown = this.dropdown = createElement('select',
  9378. void 0, {
  9379. position: 'absolute',
  9380. width: '1px',
  9381. height: '1px',
  9382. padding: 0,
  9383. border: 0,
  9384. top: '-9999em',
  9385. cursor: 'pointer',
  9386. opacity: 0.0001
  9387. },
  9388. this.div);
  9389. // Prevent page zoom on iPhone
  9390. addEvent(dropdown, 'touchstart', function () {
  9391. dropdown.style.fontSize = '16px';
  9392. });
  9393. // Forward events from select to button
  9394. [
  9395. [H.isMS ? 'mouseover' : 'mouseenter'],
  9396. [H.isMS ? 'mouseout' : 'mouseleave'],
  9397. ['change', 'click']
  9398. ].forEach(function (_a) {
  9399. var from = _a[0],
  9400. to = _a[1];
  9401. addEvent(dropdown, from, function () {
  9402. var button = buttons[_this.currentButtonIndex()];
  9403. if (button) {
  9404. fireEvent(button.element, to || from);
  9405. }
  9406. });
  9407. });
  9408. this.zoomText = renderer
  9409. .label((lang && lang.rangeSelectorZoom) || '', 0)
  9410. .attr({
  9411. padding: options.buttonTheme.padding,
  9412. height: options.buttonTheme.height,
  9413. paddingLeft: 0,
  9414. paddingRight: 0
  9415. })
  9416. .add(this.buttonGroup);
  9417. if (!this.chart.styledMode) {
  9418. this.zoomText.css(options.labelStyle);
  9419. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  9420. }
  9421. createElement('option', {
  9422. textContent: this.zoomText.textStr,
  9423. disabled: true
  9424. }, void 0, dropdown);
  9425. this.buttonOptions.forEach(function (rangeOptions, i) {
  9426. createElement('option', {
  9427. textContent: rangeOptions.title || rangeOptions.text
  9428. }, void 0, dropdown);
  9429. buttons[i] = renderer
  9430. .button(rangeOptions.text, 0, 0, function (e) {
  9431. // extract events from button object and call
  9432. var buttonEvents = (rangeOptions.events &&
  9433. rangeOptions.events.click),
  9434. callDefaultEvent;
  9435. if (buttonEvents) {
  9436. callDefaultEvent =
  9437. buttonEvents.call(rangeOptions, e);
  9438. }
  9439. if (callDefaultEvent !== false) {
  9440. _this.clickButton(i);
  9441. }
  9442. _this.isActive = true;
  9443. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  9444. .attr({
  9445. 'text-align': 'center',
  9446. width: width
  9447. })
  9448. .add(_this.buttonGroup);
  9449. if (rangeOptions.title) {
  9450. buttons[i].attr('title', rangeOptions.title);
  9451. }
  9452. });
  9453. };
  9454. /**
  9455. * Align the elements horizontally and vertically.
  9456. *
  9457. * @private
  9458. * @function Highcharts.RangeSelector#alignElements
  9459. * @return {void}
  9460. */
  9461. RangeSelector.prototype.alignElements = function () {
  9462. var _this = this;
  9463. var _a = this,
  9464. buttonGroup = _a.buttonGroup,
  9465. buttons = _a.buttons,
  9466. chart = _a.chart,
  9467. group = _a.group,
  9468. inputGroup = _a.inputGroup,
  9469. options = _a.options,
  9470. zoomText = _a.zoomText;
  9471. var chartOptions = chart.options;
  9472. var navButtonOptions = (chartOptions.exporting &&
  9473. chartOptions.exporting.enabled !== false &&
  9474. chartOptions.navigation &&
  9475. chartOptions.navigation.buttonOptions);
  9476. var buttonPosition = options.buttonPosition,
  9477. inputPosition = options.inputPosition,
  9478. verticalAlign = options.verticalAlign;
  9479. // Get the X offset required to avoid overlapping with the exporting
  9480. // button. This is is used both by the buttonGroup and the inputGroup.
  9481. var getXOffsetForExportButton = function (group,
  9482. position) {
  9483. if (navButtonOptions &&
  9484. _this.titleCollision(chart) &&
  9485. verticalAlign === 'top' &&
  9486. position.align === 'right' && ((position.y -
  9487. group.getBBox().height - 12) <
  9488. ((navButtonOptions.y || 0) +
  9489. (navButtonOptions.height || 0) +
  9490. chart.spacing[0]))) {
  9491. return -40;
  9492. }
  9493. return 0;
  9494. };
  9495. var plotLeft = chart.plotLeft;
  9496. if (group && buttonPosition && inputPosition) {
  9497. var translateX = buttonPosition.x - chart.spacing[3];
  9498. if (buttonGroup) {
  9499. this.positionButtons();
  9500. if (!this.initialButtonGroupWidth) {
  9501. var width_1 = 0;
  9502. if (zoomText) {
  9503. width_1 += zoomText.getBBox().width + 5;
  9504. }
  9505. buttons.forEach(function (button, i) {
  9506. width_1 += button.width;
  9507. if (i !== buttons.length - 1) {
  9508. width_1 += options.buttonSpacing;
  9509. }
  9510. });
  9511. this.initialButtonGroupWidth = width_1;
  9512. }
  9513. plotLeft -= chart.spacing[3];
  9514. this.updateButtonStates();
  9515. // Detect collision between button group and exporting
  9516. var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
  9517. buttonPosition);
  9518. this.alignButtonGroup(xOffsetForExportButton_1);
  9519. // Skip animation
  9520. group.placed = buttonGroup.placed = chart.hasLoaded;
  9521. }
  9522. var xOffsetForExportButton = 0;
  9523. if (inputGroup) {
  9524. // Detect collision between the input group and exporting button
  9525. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  9526. if (inputPosition.align === 'left') {
  9527. translateX = plotLeft;
  9528. }
  9529. else if (inputPosition.align === 'right') {
  9530. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  9531. }
  9532. // Update the alignment to the updated spacing box
  9533. inputGroup.align({
  9534. y: inputPosition.y,
  9535. width: inputGroup.getBBox().width,
  9536. align: inputPosition.align,
  9537. // fix wrong getBBox() value on right align
  9538. x: inputPosition.x + translateX - 2
  9539. }, true, chart.spacingBox);
  9540. // Skip animation
  9541. inputGroup.placed = chart.hasLoaded;
  9542. }
  9543. this.handleCollision(xOffsetForExportButton);
  9544. // Vertical align
  9545. group.align({
  9546. verticalAlign: verticalAlign
  9547. }, true, chart.spacingBox);
  9548. var alignTranslateY = group.alignAttr.translateY;
  9549. // Set position
  9550. var groupHeight = group.getBBox().height + 20; // # 20 padding
  9551. var translateY = 0;
  9552. // Calculate bottom position
  9553. if (verticalAlign === 'bottom') {
  9554. var legendOptions = chart.legend && chart.legend.options;
  9555. var legendHeight = (legendOptions &&
  9556. legendOptions.verticalAlign === 'bottom' &&
  9557. legendOptions.enabled &&
  9558. !legendOptions.floating ?
  9559. (chart.legend.legendHeight +
  9560. pick(legendOptions.margin, 10)) :
  9561. 0);
  9562. groupHeight = groupHeight + legendHeight - 20;
  9563. translateY = (alignTranslateY -
  9564. groupHeight -
  9565. (options.floating ? 0 : options.y) -
  9566. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  9567. 10 // 10 spacing
  9568. );
  9569. }
  9570. if (verticalAlign === 'top') {
  9571. if (options.floating) {
  9572. translateY = 0;
  9573. }
  9574. if (chart.titleOffset && chart.titleOffset[0]) {
  9575. translateY = chart.titleOffset[0];
  9576. }
  9577. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  9578. }
  9579. else if (verticalAlign === 'middle') {
  9580. if (inputPosition.y === buttonPosition.y) {
  9581. translateY = alignTranslateY;
  9582. }
  9583. else if (inputPosition.y || buttonPosition.y) {
  9584. if (inputPosition.y < 0 ||
  9585. buttonPosition.y < 0) {
  9586. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  9587. }
  9588. else {
  9589. translateY = alignTranslateY - groupHeight;
  9590. }
  9591. }
  9592. }
  9593. group.translate(options.x, options.y + Math.floor(translateY));
  9594. // Translate HTML inputs
  9595. var _b = this,
  9596. minInput = _b.minInput,
  9597. maxInput = _b.maxInput,
  9598. dropdown = _b.dropdown;
  9599. if (options.inputEnabled && minInput && maxInput) {
  9600. minInput.style.marginTop = group.translateY + 'px';
  9601. maxInput.style.marginTop = group.translateY + 'px';
  9602. }
  9603. if (dropdown) {
  9604. dropdown.style.marginTop = group.translateY + 'px';
  9605. }
  9606. }
  9607. };
  9608. /**
  9609. * Align the button group horizontally and vertically.
  9610. *
  9611. * @private
  9612. * @function Highcharts.RangeSelector#alignButtonGroup
  9613. * @param {number} xOffsetForExportButton
  9614. * @param {number} [width]
  9615. * @return {void}
  9616. */
  9617. RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
  9618. var _a = this,
  9619. chart = _a.chart,
  9620. options = _a.options,
  9621. buttonGroup = _a.buttonGroup,
  9622. buttons = _a.buttons;
  9623. var buttonPosition = options.buttonPosition;
  9624. var plotLeft = chart.plotLeft - chart.spacing[3];
  9625. var translateX = buttonPosition.x - chart.spacing[3];
  9626. if (buttonPosition.align === 'right') {
  9627. translateX += xOffsetForExportButton - plotLeft; // #13014
  9628. }
  9629. else if (buttonPosition.align === 'center') {
  9630. translateX -= plotLeft / 2;
  9631. }
  9632. if (buttonGroup) {
  9633. // Align button group
  9634. buttonGroup.align({
  9635. y: buttonPosition.y,
  9636. width: pick(width, this.initialButtonGroupWidth),
  9637. align: buttonPosition.align,
  9638. x: translateX
  9639. }, true, chart.spacingBox);
  9640. }
  9641. };
  9642. /**
  9643. * @private
  9644. * @function Highcharts.RangeSelector#positionButtons
  9645. * @return {void}
  9646. */
  9647. RangeSelector.prototype.positionButtons = function () {
  9648. var _a = this,
  9649. buttons = _a.buttons,
  9650. chart = _a.chart,
  9651. options = _a.options,
  9652. zoomText = _a.zoomText;
  9653. var verb = chart.hasLoaded ? 'animate' : 'attr';
  9654. var buttonPosition = options.buttonPosition;
  9655. var plotLeft = chart.plotLeft;
  9656. var buttonLeft = plotLeft;
  9657. if (zoomText && zoomText.visibility !== 'hidden') {
  9658. // #8769, allow dynamically updating margins
  9659. zoomText[verb]({
  9660. x: pick(plotLeft + buttonPosition.x, plotLeft)
  9661. });
  9662. // Button start position
  9663. buttonLeft += buttonPosition.x +
  9664. zoomText.getBBox().width + 5;
  9665. }
  9666. this.buttonOptions.forEach(function (rangeOptions, i) {
  9667. if (buttons[i].visibility !== 'hidden') {
  9668. buttons[i][verb]({ x: buttonLeft });
  9669. // increase button position for the next button
  9670. buttonLeft += buttons[i].width + options.buttonSpacing;
  9671. }
  9672. else {
  9673. buttons[i][verb]({ x: plotLeft });
  9674. }
  9675. });
  9676. };
  9677. /**
  9678. * Handle collision between the button group and the input group
  9679. *
  9680. * @private
  9681. * @function Highcharts.RangeSelector#handleCollision
  9682. *
  9683. * @param {number} xOffsetForExportButton
  9684. * The X offset of the group required to make room for the
  9685. * exporting button
  9686. * @return {void}
  9687. */
  9688. RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
  9689. var _this = this;
  9690. var _a = this,
  9691. chart = _a.chart,
  9692. buttonGroup = _a.buttonGroup,
  9693. inputGroup = _a.inputGroup;
  9694. var _b = this.options,
  9695. buttonPosition = _b.buttonPosition,
  9696. dropdown = _b.dropdown,
  9697. inputPosition = _b.inputPosition;
  9698. var maxButtonWidth = function () {
  9699. var buttonWidth = 0;
  9700. _this.buttons.forEach(function (button) {
  9701. var bBox = button.getBBox();
  9702. if (bBox.width > buttonWidth) {
  9703. buttonWidth = bBox.width;
  9704. }
  9705. });
  9706. return buttonWidth;
  9707. };
  9708. var groupsOverlap = function (buttonGroupWidth) {
  9709. if (inputGroup && buttonGroup) {
  9710. var inputGroupX = (inputGroup.alignAttr.translateX +
  9711. inputGroup.alignOptions.x -
  9712. xOffsetForExportButton +
  9713. // getBBox for detecing left margin
  9714. inputGroup.getBBox().x +
  9715. // 2px padding to not overlap input and label
  9716. 2);
  9717. var inputGroupWidth = inputGroup.alignOptions.width;
  9718. var buttonGroupX = buttonGroup.alignAttr.translateX +
  9719. buttonGroup.getBBox().x;
  9720. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  9721. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  9722. (buttonPosition.y <
  9723. (inputPosition.y +
  9724. inputGroup.getBBox().height));
  9725. }
  9726. return false;
  9727. };
  9728. var moveInputsDown = function () {
  9729. if (inputGroup && buttonGroup) {
  9730. inputGroup.attr({
  9731. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  9732. 0 :
  9733. -xOffsetForExportButton),
  9734. translateY: inputGroup.alignAttr.translateY +
  9735. buttonGroup.getBBox().height + 10
  9736. });
  9737. }
  9738. };
  9739. if (buttonGroup) {
  9740. if (dropdown === 'always') {
  9741. this.collapseButtons(xOffsetForExportButton);
  9742. if (groupsOverlap(maxButtonWidth())) {
  9743. // Move the inputs down if there is still a collision
  9744. // after collapsing the buttons
  9745. moveInputsDown();
  9746. }
  9747. return;
  9748. }
  9749. if (dropdown === 'never') {
  9750. this.expandButtons();
  9751. }
  9752. }
  9753. // Detect collision
  9754. if (inputGroup && buttonGroup) {
  9755. if ((inputPosition.align === buttonPosition.align) ||
  9756. // 20 is minimal spacing between elements
  9757. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  9758. if (dropdown === 'responsive') {
  9759. this.collapseButtons(xOffsetForExportButton);
  9760. if (groupsOverlap(maxButtonWidth())) {
  9761. moveInputsDown();
  9762. }
  9763. }
  9764. else {
  9765. moveInputsDown();
  9766. }
  9767. }
  9768. else if (dropdown === 'responsive') {
  9769. this.expandButtons();
  9770. }
  9771. }
  9772. else if (buttonGroup && dropdown === 'responsive') {
  9773. if (this.initialButtonGroupWidth > chart.plotWidth) {
  9774. this.collapseButtons(xOffsetForExportButton);
  9775. }
  9776. else {
  9777. this.expandButtons();
  9778. }
  9779. }
  9780. };
  9781. /**
  9782. * Collapse the buttons and put the select element on top.
  9783. *
  9784. * @private
  9785. * @function Highcharts.RangeSelector#collapseButtons
  9786. * @param {number} xOffsetForExportButton
  9787. * @return {void}
  9788. */
  9789. RangeSelector.prototype.collapseButtons = function (xOffsetForExportButton) {
  9790. var _a = this,
  9791. buttons = _a.buttons,
  9792. buttonOptions = _a.buttonOptions,
  9793. chart = _a.chart,
  9794. dropdown = _a.dropdown,
  9795. options = _a.options,
  9796. zoomText = _a.zoomText;
  9797. var userButtonTheme = (chart.userOptions.rangeSelector &&
  9798. chart.userOptions.rangeSelector.buttonTheme) || {};
  9799. var getAttribs = function (text) { return ({
  9800. text: text ? text + " \u25BE" : '▾',
  9801. width: 'auto',
  9802. paddingLeft: pick(options.buttonTheme.paddingLeft,
  9803. userButtonTheme.padding, 8),
  9804. paddingRight: pick(options.buttonTheme.paddingRight,
  9805. userButtonTheme.padding, 8)
  9806. }); };
  9807. if (zoomText) {
  9808. zoomText.hide();
  9809. }
  9810. var hasActiveButton = false;
  9811. buttonOptions.forEach(function (rangeOptions, i) {
  9812. var button = buttons[i];
  9813. if (button.state !== 2) {
  9814. button.hide();
  9815. }
  9816. else {
  9817. button.show();
  9818. button.attr(getAttribs(rangeOptions.text));
  9819. hasActiveButton = true;
  9820. }
  9821. });
  9822. if (!hasActiveButton) {
  9823. if (dropdown) {
  9824. dropdown.selectedIndex = 0;
  9825. }
  9826. buttons[0].show();
  9827. buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));
  9828. }
  9829. var align = options.buttonPosition.align;
  9830. this.positionButtons();
  9831. if (align === 'right' || align === 'center') {
  9832. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  9833. }
  9834. this.showDropdown();
  9835. };
  9836. /**
  9837. * Show all the buttons and hide the select element.
  9838. *
  9839. * @private
  9840. * @function Highcharts.RangeSelector#expandButtons
  9841. * @return {void}
  9842. */
  9843. RangeSelector.prototype.expandButtons = function () {
  9844. var _a = this,
  9845. buttons = _a.buttons,
  9846. buttonOptions = _a.buttonOptions,
  9847. options = _a.options,
  9848. zoomText = _a.zoomText;
  9849. this.hideDropdown();
  9850. if (zoomText) {
  9851. zoomText.show();
  9852. }
  9853. buttonOptions.forEach(function (rangeOptions, i) {
  9854. var button = buttons[i];
  9855. button.show();
  9856. button.attr({
  9857. text: rangeOptions.text,
  9858. width: options.buttonTheme.width || 28,
  9859. paddingLeft: pick(options.buttonTheme.paddingLeft, 'unset'),
  9860. paddingRight: pick(options.buttonTheme.paddingRight, 'unset')
  9861. });
  9862. if (button.state < 2) {
  9863. button.setState(0);
  9864. }
  9865. });
  9866. this.positionButtons();
  9867. };
  9868. /**
  9869. * Get the index of the visible button when the buttons are collapsed.
  9870. *
  9871. * @private
  9872. * @function Highcharts.RangeSelector#currentButtonIndex
  9873. * @return {number}
  9874. */
  9875. RangeSelector.prototype.currentButtonIndex = function () {
  9876. var dropdown = this.dropdown;
  9877. if (dropdown && dropdown.selectedIndex > 0) {
  9878. return dropdown.selectedIndex - 1;
  9879. }
  9880. return 0;
  9881. };
  9882. /**
  9883. * Position the select element on top of the button.
  9884. *
  9885. * @private
  9886. * @function Highcharts.RangeSelector#showDropdown
  9887. * @return {void}
  9888. */
  9889. RangeSelector.prototype.showDropdown = function () {
  9890. var _a = this,
  9891. buttonGroup = _a.buttonGroup,
  9892. buttons = _a.buttons,
  9893. chart = _a.chart,
  9894. dropdown = _a.dropdown;
  9895. if (buttonGroup && dropdown) {
  9896. var translateX = buttonGroup.translateX,
  9897. translateY = buttonGroup.translateY;
  9898. var bBox = buttons[this.currentButtonIndex()].getBBox();
  9899. css(dropdown, {
  9900. left: (chart.plotLeft + translateX) + 'px',
  9901. top: (translateY + 0.5) + 'px',
  9902. width: bBox.width + 'px',
  9903. height: bBox.height + 'px'
  9904. });
  9905. this.hasVisibleDropdown = true;
  9906. }
  9907. };
  9908. /**
  9909. * @private
  9910. * @function Highcharts.RangeSelector#hideDropdown
  9911. * @return {void}
  9912. */
  9913. RangeSelector.prototype.hideDropdown = function () {
  9914. var dropdown = this.dropdown;
  9915. if (dropdown) {
  9916. css(dropdown, {
  9917. top: '-9999em',
  9918. width: '1px',
  9919. height: '1px'
  9920. });
  9921. this.hasVisibleDropdown = false;
  9922. }
  9923. };
  9924. /**
  9925. * Extracts height of range selector
  9926. *
  9927. * @private
  9928. * @function Highcharts.RangeSelector#getHeight
  9929. * @return {number}
  9930. * Returns rangeSelector height
  9931. */
  9932. RangeSelector.prototype.getHeight = function () {
  9933. var rangeSelector = this,
  9934. options = rangeSelector.options,
  9935. rangeSelectorGroup = rangeSelector.group,
  9936. inputPosition = options.inputPosition,
  9937. buttonPosition = options.buttonPosition,
  9938. yPosition = options.y,
  9939. buttonPositionY = buttonPosition.y,
  9940. inputPositionY = inputPosition.y,
  9941. rangeSelectorHeight = 0,
  9942. minPosition;
  9943. if (options.height) {
  9944. return options.height;
  9945. }
  9946. // Align the elements before we read the height in case we're switching
  9947. // between wrapped and non-wrapped layout
  9948. this.alignElements();
  9949. rangeSelectorHeight = rangeSelectorGroup ?
  9950. // 13px to keep back compatibility
  9951. (rangeSelectorGroup.getBBox(true).height) + 13 +
  9952. yPosition :
  9953. 0;
  9954. minPosition = Math.min(inputPositionY, buttonPositionY);
  9955. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  9956. (inputPositionY > 0 && buttonPositionY > 0)) {
  9957. rangeSelectorHeight += Math.abs(minPosition);
  9958. }
  9959. return rangeSelectorHeight;
  9960. };
  9961. /**
  9962. * Detect collision with title or subtitle
  9963. *
  9964. * @private
  9965. * @function Highcharts.RangeSelector#titleCollision
  9966. *
  9967. * @param {Highcharts.Chart} chart
  9968. *
  9969. * @return {boolean}
  9970. * Returns collision status
  9971. */
  9972. RangeSelector.prototype.titleCollision = function (chart) {
  9973. return !(chart.options.title.text ||
  9974. chart.options.subtitle.text);
  9975. };
  9976. /**
  9977. * Update the range selector with new options
  9978. *
  9979. * @private
  9980. * @function Highcharts.RangeSelector#update
  9981. * @param {Highcharts.RangeSelectorOptions} options
  9982. * @return {void}
  9983. */
  9984. RangeSelector.prototype.update = function (options) {
  9985. var chart = this.chart;
  9986. merge(true, chart.options.rangeSelector, options);
  9987. this.destroy();
  9988. this.init(chart);
  9989. this.render();
  9990. };
  9991. /**
  9992. * Destroys allocated elements.
  9993. *
  9994. * @private
  9995. * @function Highcharts.RangeSelector#destroy
  9996. */
  9997. RangeSelector.prototype.destroy = function () {
  9998. var rSelector = this,
  9999. minInput = rSelector.minInput,
  10000. maxInput = rSelector.maxInput;
  10001. if (rSelector.eventsToUnbind) {
  10002. rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
  10003. rSelector.eventsToUnbind = void 0;
  10004. }
  10005. // Destroy elements in collections
  10006. destroyObjectProperties(rSelector.buttons);
  10007. // Clear input element events
  10008. if (minInput) {
  10009. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  10010. }
  10011. if (maxInput) {
  10012. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  10013. }
  10014. // Destroy HTML and SVG elements
  10015. objectEach(rSelector, function (val, key) {
  10016. if (val && key !== 'chart') {
  10017. if (val instanceof SVGElement) {
  10018. // SVGElement
  10019. val.destroy();
  10020. }
  10021. else if (val instanceof window.HTMLElement) {
  10022. // HTML element
  10023. discardElement(val);
  10024. }
  10025. }
  10026. if (val !== RangeSelector.prototype[key]) {
  10027. rSelector[key] = null;
  10028. }
  10029. }, this);
  10030. };
  10031. return RangeSelector;
  10032. }());
  10033. /**
  10034. * The default buttons for pre-selecting time frames
  10035. */
  10036. RangeSelector.prototype.defaultButtons = [{
  10037. type: 'month',
  10038. count: 1,
  10039. text: '1m',
  10040. title: 'View 1 month'
  10041. }, {
  10042. type: 'month',
  10043. count: 3,
  10044. text: '3m',
  10045. title: 'View 3 months'
  10046. }, {
  10047. type: 'month',
  10048. count: 6,
  10049. text: '6m',
  10050. title: 'View 6 months'
  10051. }, {
  10052. type: 'ytd',
  10053. text: 'YTD',
  10054. title: 'View year to date'
  10055. }, {
  10056. type: 'year',
  10057. count: 1,
  10058. text: '1y',
  10059. title: 'View 1 year'
  10060. }, {
  10061. type: 'all',
  10062. text: 'All',
  10063. title: 'View all'
  10064. }];
  10065. /**
  10066. * The date formats to use when setting min, max and value on date inputs
  10067. */
  10068. RangeSelector.prototype.inputTypeFormats = {
  10069. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  10070. 'date': '%Y-%m-%d',
  10071. 'time': '%H:%M:%S'
  10072. };
  10073. /**
  10074. * Get the preferred input type based on a date format string.
  10075. *
  10076. * @private
  10077. * @function preferredInputType
  10078. * @param {string} format
  10079. * @return {string}
  10080. */
  10081. function preferredInputType(format) {
  10082. var ms = format.indexOf('%L') !== -1;
  10083. if (ms) {
  10084. return 'text';
  10085. }
  10086. var date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y'].some(function (char) {
  10087. return format.indexOf('%' + char) !== -1;
  10088. });
  10089. var time = ['H', 'k', 'I', 'l', 'M', 'S'].some(function (char) {
  10090. return format.indexOf('%' + char) !== -1;
  10091. });
  10092. if (date && time) {
  10093. return 'datetime-local';
  10094. }
  10095. if (date) {
  10096. return 'date';
  10097. }
  10098. if (time) {
  10099. return 'time';
  10100. }
  10101. return 'text';
  10102. }
  10103. /**
  10104. * Get the axis min value based on the range option and the current max. For
  10105. * stock charts this is extended via the {@link RangeSelector} so that if the
  10106. * selected range is a multiple of months or years, it is compensated for
  10107. * various month lengths.
  10108. *
  10109. * @private
  10110. * @function Highcharts.Axis#minFromRange
  10111. * @return {number|undefined}
  10112. * The new minimum value.
  10113. */
  10114. Axis.prototype.minFromRange = function () {
  10115. var rangeOptions = this.range,
  10116. type = rangeOptions.type,
  10117. min,
  10118. max = this.max,
  10119. dataMin,
  10120. range,
  10121. time = this.chart.time,
  10122. // Get the true range from a start date
  10123. getTrueRange = function (base,
  10124. count) {
  10125. var timeName = type === 'year' ? 'FullYear' : 'Month';
  10126. var date = new time.Date(base);
  10127. var basePeriod = time.get(timeName,
  10128. date);
  10129. time.set(timeName, date, basePeriod + count);
  10130. if (basePeriod === time.get(timeName, date)) {
  10131. time.set('Date', date, 0); // #6537
  10132. }
  10133. return date.getTime() - base;
  10134. };
  10135. if (isNumber(rangeOptions)) {
  10136. min = max - rangeOptions;
  10137. range = rangeOptions;
  10138. }
  10139. else {
  10140. min = max + getTrueRange(max, -rangeOptions.count);
  10141. // Let the fixedRange reflect initial settings (#5930)
  10142. if (this.chart) {
  10143. this.chart.fixedRange = max - min;
  10144. }
  10145. }
  10146. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  10147. if (!isNumber(min)) {
  10148. min = dataMin;
  10149. }
  10150. if (min <= dataMin) {
  10151. min = dataMin;
  10152. if (typeof range === 'undefined') { // #4501
  10153. range = getTrueRange(min, rangeOptions.count);
  10154. }
  10155. this.newMax = Math.min(min + range, this.dataMax);
  10156. }
  10157. if (!isNumber(max)) {
  10158. min = void 0;
  10159. }
  10160. return min;
  10161. };
  10162. if (!H.RangeSelector) {
  10163. var chartDestroyEvents_1 = [];
  10164. var initRangeSelector_1 = function (chart) {
  10165. var extremes,
  10166. rangeSelector = chart.rangeSelector,
  10167. legend,
  10168. alignTo,
  10169. verticalAlign;
  10170. /**
  10171. * @private
  10172. */
  10173. function render() {
  10174. if (rangeSelector) {
  10175. extremes = chart.xAxis[0].getExtremes();
  10176. legend = chart.legend;
  10177. verticalAlign = (rangeSelector &&
  10178. rangeSelector.options.verticalAlign);
  10179. if (isNumber(extremes.min)) {
  10180. rangeSelector.render(extremes.min, extremes.max);
  10181. }
  10182. // Re-align the legend so that it's below the rangeselector
  10183. if (legend.display &&
  10184. verticalAlign === 'top' &&
  10185. verticalAlign === legend.options.verticalAlign) {
  10186. // Create a new alignment box for the legend.
  10187. alignTo = merge(chart.spacingBox);
  10188. if (legend.options.layout === 'vertical') {
  10189. alignTo.y = chart.plotTop;
  10190. }
  10191. else {
  10192. alignTo.y += rangeSelector.getHeight();
  10193. }
  10194. legend.group.placed = false; // Don't animate the alignment.
  10195. legend.align(alignTo);
  10196. }
  10197. }
  10198. }
  10199. if (rangeSelector) {
  10200. var events = find(chartDestroyEvents_1,
  10201. function (e) { return e[0] === chart; });
  10202. if (!events) {
  10203. chartDestroyEvents_1.push([chart, [
  10204. // redraw the scroller on setExtremes
  10205. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  10206. if (rangeSelector) {
  10207. rangeSelector.render(e.min, e.max);
  10208. }
  10209. }),
  10210. // redraw the scroller chart resize
  10211. addEvent(chart, 'redraw', render)
  10212. ]]);
  10213. }
  10214. // do it now
  10215. render();
  10216. }
  10217. };
  10218. // Initialize rangeselector for stock charts
  10219. addEvent(Chart, 'afterGetContainer', function () {
  10220. if (this.options.rangeSelector &&
  10221. this.options.rangeSelector.enabled) {
  10222. this.rangeSelector = new RangeSelector(this);
  10223. }
  10224. });
  10225. addEvent(Chart, 'beforeRender', function () {
  10226. var chart = this,
  10227. axes = chart.axes,
  10228. rangeSelector = chart.rangeSelector,
  10229. verticalAlign;
  10230. if (rangeSelector) {
  10231. if (isNumber(rangeSelector.deferredYTDClick)) {
  10232. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  10233. delete rangeSelector.deferredYTDClick;
  10234. }
  10235. axes.forEach(function (axis) {
  10236. axis.updateNames();
  10237. axis.setScale();
  10238. });
  10239. chart.getAxisMargins();
  10240. rangeSelector.render();
  10241. verticalAlign = rangeSelector.options.verticalAlign;
  10242. if (!rangeSelector.options.floating) {
  10243. if (verticalAlign === 'bottom') {
  10244. this.extraBottomMargin = true;
  10245. }
  10246. else if (verticalAlign !== 'middle') {
  10247. this.extraTopMargin = true;
  10248. }
  10249. }
  10250. }
  10251. });
  10252. addEvent(Chart, 'update', function (e) {
  10253. var chart = this,
  10254. options = e.options,
  10255. optionsRangeSelector = options.rangeSelector,
  10256. rangeSelector = chart.rangeSelector,
  10257. verticalAlign,
  10258. extraBottomMarginWas = this.extraBottomMargin,
  10259. extraTopMarginWas = this.extraTopMargin;
  10260. if (optionsRangeSelector &&
  10261. optionsRangeSelector.enabled &&
  10262. !defined(rangeSelector) &&
  10263. this.options.rangeSelector) {
  10264. this.options.rangeSelector.enabled = true;
  10265. this.rangeSelector = rangeSelector = new RangeSelector(this);
  10266. }
  10267. this.extraBottomMargin = false;
  10268. this.extraTopMargin = false;
  10269. if (rangeSelector) {
  10270. initRangeSelector_1(this);
  10271. verticalAlign = (optionsRangeSelector &&
  10272. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  10273. if (!rangeSelector.options.floating) {
  10274. if (verticalAlign === 'bottom') {
  10275. this.extraBottomMargin = true;
  10276. }
  10277. else if (verticalAlign !== 'middle') {
  10278. this.extraTopMargin = true;
  10279. }
  10280. }
  10281. if (this.extraBottomMargin !== extraBottomMarginWas ||
  10282. this.extraTopMargin !== extraTopMarginWas) {
  10283. this.isDirtyBox = true;
  10284. }
  10285. }
  10286. });
  10287. addEvent(Chart, 'render', function () {
  10288. var chart = this,
  10289. rangeSelector = chart.rangeSelector,
  10290. verticalAlign;
  10291. if (rangeSelector && !rangeSelector.options.floating) {
  10292. rangeSelector.render();
  10293. verticalAlign = rangeSelector.options.verticalAlign;
  10294. if (verticalAlign === 'bottom') {
  10295. this.extraBottomMargin = true;
  10296. }
  10297. else if (verticalAlign !== 'middle') {
  10298. this.extraTopMargin = true;
  10299. }
  10300. }
  10301. });
  10302. addEvent(Chart, 'getMargins', function () {
  10303. var rangeSelector = this.rangeSelector,
  10304. rangeSelectorHeight;
  10305. if (rangeSelector) {
  10306. rangeSelectorHeight = rangeSelector.getHeight();
  10307. if (this.extraTopMargin) {
  10308. this.plotTop += rangeSelectorHeight;
  10309. }
  10310. if (this.extraBottomMargin) {
  10311. this.marginBottom += rangeSelectorHeight;
  10312. }
  10313. }
  10314. });
  10315. Chart.prototype.callbacks.push(initRangeSelector_1);
  10316. // Remove resize/afterSetExtremes at chart destroy
  10317. addEvent(Chart, 'destroy', function destroyEvents() {
  10318. for (var i = 0; i < chartDestroyEvents_1.length; i++) {
  10319. var events = chartDestroyEvents_1[i];
  10320. if (events[0] === this) {
  10321. events[1].forEach(function (unbind) { return unbind(); });
  10322. chartDestroyEvents_1.splice(i, 1);
  10323. return;
  10324. }
  10325. }
  10326. });
  10327. H.RangeSelector = RangeSelector;
  10328. }
  10329. return RangeSelector;
  10330. });
  10331. _registerModule(_modules, 'Core/Chart/StockChart.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/FormatUtilities.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (A, Axis, Chart, F, D, Palette, Point, Series, SVGRenderer, U) {
  10332. /* *
  10333. *
  10334. * (c) 2010-2021 Torstein Honsi
  10335. *
  10336. * License: www.highcharts.com/license
  10337. *
  10338. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10339. *
  10340. * */
  10341. var __extends = (this && this.__extends) || (function () {
  10342. var extendStatics = function (d,
  10343. b) {
  10344. extendStatics = Object.setPrototypeOf ||
  10345. ({ __proto__: [] } instanceof Array && function (d,
  10346. b) { d.__proto__ = b; }) ||
  10347. function (d,
  10348. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  10349. return extendStatics(d, b);
  10350. };
  10351. return function (d, b) {
  10352. extendStatics(d, b);
  10353. function __() { this.constructor = d; }
  10354. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  10355. };
  10356. })();
  10357. var animObject = A.animObject;
  10358. var format = F.format;
  10359. var getOptions = D.getOptions;
  10360. var pointTooltipFormatter = Point.prototype.tooltipFormatter;
  10361. var _a = Series.prototype,
  10362. seriesInit = _a.init,
  10363. seriesProcessData = _a.processData;
  10364. var addEvent = U.addEvent,
  10365. arrayMax = U.arrayMax,
  10366. arrayMin = U.arrayMin,
  10367. clamp = U.clamp,
  10368. defined = U.defined,
  10369. extend = U.extend,
  10370. find = U.find,
  10371. isNumber = U.isNumber,
  10372. isString = U.isString,
  10373. merge = U.merge,
  10374. pick = U.pick,
  10375. splat = U.splat;
  10376. // Has a dependency on Navigator due to the use of
  10377. // defaultOptions.navigator
  10378. // Has a dependency on Scrollbar due to the use of
  10379. // defaultOptions.scrollbar
  10380. // Has a dependency on RangeSelector due to the use of
  10381. // defaultOptions.rangeSelector
  10382. /* *
  10383. *
  10384. * Class
  10385. *
  10386. * */
  10387. /**
  10388. * Stock-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
  10389. *
  10390. * @requires modules/stock
  10391. *
  10392. * @class
  10393. * @name Highcharts.StockChart
  10394. * @extends Highcharts.Chart
  10395. */
  10396. var StockChart = /** @class */ (function (_super) {
  10397. __extends(StockChart, _super);
  10398. function StockChart() {
  10399. return _super !== null && _super.apply(this, arguments) || this;
  10400. }
  10401. /**
  10402. * Initializes the chart. The constructor's arguments are passed on
  10403. * directly.
  10404. *
  10405. * @function Highcharts.StockChart#init
  10406. *
  10407. * @param {Highcharts.Options} userOptions
  10408. * Custom options.
  10409. *
  10410. * @param {Function} [callback]
  10411. * Function to run when the chart has loaded and and all external
  10412. * images are loaded.
  10413. *
  10414. * @return {void}
  10415. *
  10416. * @fires Highcharts.StockChart#event:init
  10417. * @fires Highcharts.StockChart#event:afterInit
  10418. */
  10419. StockChart.prototype.init = function (userOptions, callback) {
  10420. var defaultOptions = getOptions(),
  10421. xAxisOptions = userOptions.xAxis,
  10422. yAxisOptions = userOptions.yAxis,
  10423. // Always disable startOnTick:true on the main axis when the
  10424. // navigator is enabled (#1090)
  10425. navigatorEnabled = pick(userOptions.navigator && userOptions.navigator.enabled,
  10426. defaultOptions.navigator.enabled,
  10427. true);
  10428. // Avoid doing these twice
  10429. userOptions.xAxis = userOptions.yAxis = void 0;
  10430. var options = merge({
  10431. chart: {
  10432. panning: {
  10433. enabled: true,
  10434. type: 'x'
  10435. },
  10436. pinchType: 'x'
  10437. },
  10438. navigator: {
  10439. enabled: navigatorEnabled
  10440. },
  10441. scrollbar: {
  10442. // #4988 - check if setOptions was called
  10443. enabled: pick(defaultOptions.scrollbar && defaultOptions.scrollbar.enabled,
  10444. true)
  10445. },
  10446. rangeSelector: {
  10447. // #4988 - check if setOptions was called
  10448. enabled: pick(defaultOptions.rangeSelector.enabled,
  10449. true)
  10450. },
  10451. title: {
  10452. text: null
  10453. },
  10454. tooltip: {
  10455. split: pick(defaultOptions.tooltip.split,
  10456. true),
  10457. crosshairs: true
  10458. },
  10459. legend: {
  10460. enabled: false
  10461. }
  10462. },
  10463. userOptions, // user's options
  10464. {
  10465. isStock: true // internal flag
  10466. });
  10467. userOptions.xAxis = xAxisOptions;
  10468. userOptions.yAxis = yAxisOptions;
  10469. // apply X axis options to both single and multi y axes
  10470. options.xAxis = splat(userOptions.xAxis || {}).map(function (xAxisOptions, i) {
  10471. return merge(getDefaultAxisOptions('xAxis', xAxisOptions), defaultOptions.xAxis, // #3802
  10472. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  10473. xAxisOptions, // user options
  10474. getForcedAxisOptions('xAxis', userOptions));
  10475. });
  10476. // apply Y axis options to both single and multi y axes
  10477. options.yAxis = splat(userOptions.yAxis || {}).map(function (yAxisOptions, i) {
  10478. return merge(getDefaultAxisOptions('yAxis', yAxisOptions), defaultOptions.yAxis, // #3802
  10479. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  10480. yAxisOptions // user options
  10481. );
  10482. });
  10483. _super.prototype.init.call(this, options, callback);
  10484. };
  10485. /**
  10486. * Factory for creating different axis types.
  10487. * Extended to add stock defaults.
  10488. *
  10489. * @private
  10490. * @function Highcharts.StockChart#createAxis
  10491. *
  10492. * @param {string} type
  10493. * An axis type.
  10494. *
  10495. * @param {Chart.CreateAxisOptionsObject} options
  10496. * The axis creation options.
  10497. *
  10498. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  10499. */
  10500. StockChart.prototype.createAxis = function (type, options) {
  10501. options.axis = merge(getDefaultAxisOptions(type, options.axis), options.axis, getForcedAxisOptions(type, this.userOptions));
  10502. return _super.prototype.createAxis.call(this, type, options);
  10503. };
  10504. return StockChart;
  10505. }(Chart));
  10506. /* eslint-disable no-invalid-this, valid-jsdoc */
  10507. (function (StockChart) {
  10508. /**
  10509. * Factory function for creating new stock charts. Creates a new
  10510. * {@link Highcharts.StockChart|StockChart} object with different default
  10511. * options than the basic Chart.
  10512. *
  10513. * @example
  10514. * let chart = Highcharts.stockChart('container', {
  10515. * series: [{
  10516. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  10517. * pointInterval: 24 * 60 * 60 * 1000
  10518. * }]
  10519. * });
  10520. *
  10521. * @function Highcharts.stockChart
  10522. *
  10523. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  10524. * The DOM element to render to, or its id.
  10525. *
  10526. * @param {Highcharts.Options} options
  10527. * The chart options structure as described in the
  10528. * [options reference](https://api.highcharts.com/highstock).
  10529. *
  10530. * @param {Highcharts.ChartCallbackFunction} [callback]
  10531. * A function to execute when the chart object is finished loading
  10532. * and rendering. In most cases the chart is built in one thread,
  10533. * but in Internet Explorer version 8 or less the chart is sometimes
  10534. * initialized before the document is ready, and in these cases the
  10535. * chart object will not be finished synchronously. As a
  10536. * consequence, code that relies on the newly built Chart object
  10537. * should always run in the callback. Defining a
  10538. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  10539. * handler is equivalent.
  10540. *
  10541. * @return {Highcharts.StockChart}
  10542. * The chart object.
  10543. */
  10544. function stockChart(a, b, c) {
  10545. return new StockChart(a, b, c);
  10546. }
  10547. StockChart.stockChart = stockChart;
  10548. })(StockChart || (StockChart = {}));
  10549. /**
  10550. * Get stock-specific default axis options.
  10551. *
  10552. * @private
  10553. * @function getDefaultAxisOptions
  10554. * @param {string} type
  10555. * @param {Highcharts.AxisOptions} options
  10556. * @return {Highcharts.AxisOptions}
  10557. */
  10558. function getDefaultAxisOptions(type, options) {
  10559. if (type === 'xAxis') {
  10560. return {
  10561. minPadding: 0,
  10562. maxPadding: 0,
  10563. overscroll: 0,
  10564. ordinal: true,
  10565. title: {
  10566. text: null
  10567. },
  10568. labels: {
  10569. overflow: 'justify'
  10570. },
  10571. showLastLabel: true
  10572. };
  10573. }
  10574. if (type === 'yAxis') {
  10575. return {
  10576. labels: {
  10577. y: -2
  10578. },
  10579. opposite: pick(options.opposite, true),
  10580. /**
  10581. * @default {highcharts} true
  10582. * @default {highstock} false
  10583. * @apioption yAxis.showLastLabel
  10584. *
  10585. * @private
  10586. */
  10587. showLastLabel: !!(
  10588. // #6104, show last label by default for category axes
  10589. options.categories ||
  10590. options.type === 'category'),
  10591. title: {
  10592. text: null
  10593. }
  10594. };
  10595. }
  10596. return {};
  10597. }
  10598. /**
  10599. * Get stock-specific forced axis options.
  10600. *
  10601. * @private
  10602. * @function getForcedAxisOptions
  10603. * @param {string} type
  10604. * @param {Highcharts.Options} chartOptions
  10605. * @return {Highcharts.AxisOptions}
  10606. */
  10607. function getForcedAxisOptions(type, chartOptions) {
  10608. if (type === 'xAxis') {
  10609. var defaultOptions = getOptions(),
  10610. // Always disable startOnTick:true on the main axis when the
  10611. // navigator is enabled (#1090)
  10612. navigatorEnabled = pick(chartOptions.navigator && chartOptions.navigator.enabled,
  10613. defaultOptions.navigator.enabled,
  10614. true);
  10615. var axisOptions = {
  10616. type: 'datetime',
  10617. categories: void 0
  10618. };
  10619. if (navigatorEnabled) {
  10620. axisOptions.startOnTick = false;
  10621. axisOptions.endOnTick = false;
  10622. }
  10623. return axisOptions;
  10624. }
  10625. return {};
  10626. }
  10627. /* *
  10628. *
  10629. * Compositions
  10630. *
  10631. * */
  10632. // Handle som Stock-specific series defaults, override the plotOptions before
  10633. // series options are handled.
  10634. addEvent(Series, 'setOptions', function (e) {
  10635. var overrides;
  10636. if (this.chart.options.isStock) {
  10637. if (this.is('column') || this.is('columnrange')) {
  10638. overrides = {
  10639. borderWidth: 0,
  10640. shadow: false
  10641. };
  10642. }
  10643. else if (!this.is('scatter') && !this.is('sma')) {
  10644. overrides = {
  10645. marker: {
  10646. enabled: false,
  10647. radius: 2
  10648. }
  10649. };
  10650. }
  10651. if (overrides) {
  10652. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  10653. }
  10654. }
  10655. });
  10656. // Override the automatic label alignment so that the first Y axis' labels
  10657. // are drawn on top of the grid line, and subsequent axes are drawn outside
  10658. addEvent(Axis, 'autoLabelAlign', function (e) {
  10659. var chart = this.chart,
  10660. options = this.options,
  10661. panes = chart._labelPanes = chart._labelPanes || {},
  10662. key,
  10663. labelOptions = this.options.labels;
  10664. if (this.chart.options.isStock && this.coll === 'yAxis') {
  10665. key = options.top + ',' + options.height;
  10666. // do it only for the first Y axis of each pane
  10667. if (!panes[key] && labelOptions.enabled) {
  10668. if (labelOptions.x === 15) { // default
  10669. labelOptions.x = 0;
  10670. }
  10671. if (typeof labelOptions.align === 'undefined') {
  10672. labelOptions.align = 'right';
  10673. }
  10674. panes[key] = this;
  10675. e.align = 'right';
  10676. e.preventDefault();
  10677. }
  10678. }
  10679. });
  10680. // Clear axis from label panes (#6071)
  10681. addEvent(Axis, 'destroy', function () {
  10682. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  10683. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  10684. delete chart._labelPanes[key];
  10685. }
  10686. });
  10687. // Override getPlotLinePath to allow for multipane charts
  10688. addEvent(Axis, 'getPlotLinePath', function (e) {
  10689. var axis = this,
  10690. series = (this.isLinked && !this.series ?
  10691. this.linkedParent.series :
  10692. this.series),
  10693. chart = axis.chart,
  10694. renderer = chart.renderer,
  10695. axisLeft = axis.left,
  10696. axisTop = axis.top,
  10697. x1,
  10698. y1,
  10699. x2,
  10700. y2,
  10701. result = [],
  10702. axes = [], // #3416 need a default array
  10703. axes2,
  10704. uniqueAxes,
  10705. translatedValue = e.translatedValue,
  10706. value = e.value,
  10707. force = e.force,
  10708. transVal;
  10709. /**
  10710. * Return the other axis based on either the axis option or on related
  10711. * series.
  10712. * @private
  10713. */
  10714. function getAxis(coll) {
  10715. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis',
  10716. opt = axis.options[otherColl];
  10717. // Other axis indexed by number
  10718. if (isNumber(opt)) {
  10719. return [chart[otherColl][opt]];
  10720. }
  10721. // Other axis indexed by id (like navigator)
  10722. if (isString(opt)) {
  10723. return [chart.get(opt)];
  10724. }
  10725. // Auto detect based on existing series
  10726. return series.map(function (s) {
  10727. return s[otherColl];
  10728. });
  10729. }
  10730. if ( // For stock chart, by default render paths across the panes
  10731. // except the case when `acrossPanes` is disabled by user (#6644)
  10732. (chart.options.isStock && e.acrossPanes !== false) &&
  10733. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  10734. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  10735. e.preventDefault();
  10736. // Get the related axes based on series
  10737. axes = getAxis(axis.coll);
  10738. // Get the related axes based options.*Axis setting #2810
  10739. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  10740. axes2.forEach(function (A) {
  10741. if (defined(A.options.id) ?
  10742. A.options.id.indexOf('navigator') === -1 :
  10743. true) {
  10744. var a = (A.isXAxis ? 'yAxis' : 'xAxis'),
  10745. rax = (defined(A.options[a]) ?
  10746. chart[a][A.options[a]] :
  10747. chart[a][0]);
  10748. if (axis === rax) {
  10749. axes.push(A);
  10750. }
  10751. }
  10752. });
  10753. // Remove duplicates in the axes array. If there are no axes in the axes
  10754. // array, we are adding an axis without data, so we need to populate
  10755. // this with grid lines (#2796).
  10756. uniqueAxes = axes.length ?
  10757. [] :
  10758. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  10759. axes.forEach(function (axis2) {
  10760. if (uniqueAxes.indexOf(axis2) === -1 &&
  10761. // Do not draw on axis which overlap completely. #5424
  10762. !find(uniqueAxes, function (unique) {
  10763. return unique.pos === axis2.pos && unique.len === axis2.len;
  10764. })) {
  10765. uniqueAxes.push(axis2);
  10766. }
  10767. });
  10768. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  10769. if (isNumber(transVal)) {
  10770. if (axis.horiz) {
  10771. uniqueAxes.forEach(function (axis2) {
  10772. var skip;
  10773. y1 = axis2.pos;
  10774. y2 = y1 + axis2.len;
  10775. x1 = x2 = Math.round(transVal + axis.transB);
  10776. // outside plot area
  10777. if (force !== 'pass' &&
  10778. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  10779. if (force) {
  10780. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  10781. }
  10782. else {
  10783. skip = true;
  10784. }
  10785. }
  10786. if (!skip) {
  10787. result.push(['M', x1, y1], ['L', x2, y2]);
  10788. }
  10789. });
  10790. }
  10791. else {
  10792. uniqueAxes.forEach(function (axis2) {
  10793. var skip;
  10794. x1 = axis2.pos;
  10795. x2 = x1 + axis2.len;
  10796. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  10797. // outside plot area
  10798. if (force !== 'pass' &&
  10799. (y1 < axisTop || y1 > axisTop + axis.height)) {
  10800. if (force) {
  10801. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  10802. }
  10803. else {
  10804. skip = true;
  10805. }
  10806. }
  10807. if (!skip) {
  10808. result.push(['M', x1, y1], ['L', x2, y2]);
  10809. }
  10810. });
  10811. }
  10812. }
  10813. e.path = result.length > 0 ?
  10814. renderer.crispPolyLine(result, e.lineWidth || 1) :
  10815. // #3557 getPlotLinePath in regular Highcharts also returns null
  10816. null;
  10817. }
  10818. });
  10819. /**
  10820. * Function to crisp a line with multiple segments
  10821. *
  10822. * @private
  10823. * @function Highcharts.SVGRenderer#crispPolyLine
  10824. * @param {Highcharts.SVGPathArray} points
  10825. * @param {number} width
  10826. * @return {Highcharts.SVGPathArray}
  10827. */
  10828. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  10829. // points format: [['M', 0, 0], ['L', 100, 0]]
  10830. // normalize to a crisp line
  10831. for (var i = 0; i < points.length; i = i + 2) {
  10832. var start = points[i],
  10833. end = points[i + 1];
  10834. if (start[1] === end[1]) {
  10835. // Substract due to #1129. Now bottom and left axis gridlines behave
  10836. // the same.
  10837. start[1] = end[1] =
  10838. Math.round(start[1]) - (width % 2 / 2);
  10839. }
  10840. if (start[2] === end[2]) {
  10841. start[2] = end[2] =
  10842. Math.round(start[2]) + (width % 2 / 2);
  10843. }
  10844. }
  10845. return points;
  10846. };
  10847. // Wrapper to hide the label
  10848. addEvent(Axis, 'afterHideCrosshair', function () {
  10849. if (this.crossLabel) {
  10850. this.crossLabel = this.crossLabel.hide();
  10851. }
  10852. });
  10853. // Extend crosshairs to also draw the label
  10854. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  10855. // Check if the label has to be drawn
  10856. if (!this.crosshair ||
  10857. !this.crosshair.label ||
  10858. !this.crosshair.label.enabled ||
  10859. !this.cross ||
  10860. !isNumber(this.min) ||
  10861. !isNumber(this.max)) {
  10862. return;
  10863. }
  10864. var chart = this.chart, log = this.logarithmic, options = this.crosshair.label, // the label's options
  10865. horiz = this.horiz, // axis orientation
  10866. opposite = this.opposite, // axis position
  10867. left = this.left, // left position
  10868. top = this.top, // top position
  10869. crossLabel = this.crossLabel, // the svgElement
  10870. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, offset = 0,
  10871. // Use last available event (#5287)
  10872. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  10873. if (log) {
  10874. min = log.lin2log(min);
  10875. max = log.lin2log(max);
  10876. }
  10877. align = (horiz ? 'center' : opposite ?
  10878. (this.labelAlign === 'right' ? 'right' : 'left') :
  10879. (this.labelAlign === 'left' ? 'left' : 'center'));
  10880. // If the label does not exist yet, create it.
  10881. if (!crossLabel) {
  10882. crossLabel = this.crossLabel = chart.renderer
  10883. .label('', 0, void 0, options.shape || 'callout')
  10884. .addClass('highcharts-crosshair-label highcharts-color-' + (point ?
  10885. point.series.colorIndex :
  10886. this.series[0] && this.series[0].colorIndex))
  10887. .attr({
  10888. align: options.align || align,
  10889. padding: pick(options.padding, 8),
  10890. r: pick(options.borderRadius, 3),
  10891. zIndex: 2
  10892. })
  10893. .add(this.labelGroup);
  10894. // Presentational
  10895. if (!chart.styledMode) {
  10896. crossLabel
  10897. .attr({
  10898. fill: options.backgroundColor ||
  10899. point && point.series && point.series.color || // #14888
  10900. Palette.neutralColor60,
  10901. stroke: options.borderColor || '',
  10902. 'stroke-width': options.borderWidth || 0
  10903. })
  10904. .css(extend({
  10905. color: Palette.backgroundColor,
  10906. fontWeight: 'normal',
  10907. fontSize: '11px',
  10908. textAlign: 'center'
  10909. }, options.style || {}));
  10910. }
  10911. }
  10912. if (horiz) {
  10913. posx = snap ? (point.plotX || 0) + left : e.chartX;
  10914. posy = top + (opposite ? 0 : this.height);
  10915. }
  10916. else {
  10917. posx = opposite ? this.width + left : 0;
  10918. posy = snap ? (point.plotY || 0) + top : e.chartY;
  10919. }
  10920. if (!formatOption && !options.formatter) {
  10921. if (this.dateTime) {
  10922. formatFormat = '%b %d, %Y';
  10923. }
  10924. formatOption =
  10925. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  10926. }
  10927. // Show the label
  10928. var value = snap ?
  10929. (this.isXAxis ? point.x : point.y) :
  10930. this.toValue(horiz ? e.chartX : e.chartY);
  10931. // Crosshair should be rendered within Axis range (#7219). Also, the point
  10932. // of currentPriceIndicator should be inside the plot area, #14879.
  10933. var isInside = point ?
  10934. point.series.isPointInside(point) :
  10935. (isNumber(value) && value > min && value < max);
  10936. var text = '';
  10937. if (formatOption) {
  10938. text = format(formatOption, { value: value }, chart);
  10939. }
  10940. else if (options.formatter && isNumber(value)) {
  10941. text = options.formatter.call(this, value);
  10942. }
  10943. crossLabel.attr({
  10944. text: text,
  10945. x: posx,
  10946. y: posy,
  10947. visibility: isInside ? 'visible' : 'hidden'
  10948. });
  10949. crossBox = crossLabel.getBBox();
  10950. // now it is placed we can correct its position
  10951. if (isNumber(crossLabel.y)) {
  10952. if (horiz) {
  10953. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  10954. posy = crossLabel.y - crossBox.height;
  10955. }
  10956. }
  10957. else {
  10958. posy = crossLabel.y - (crossBox.height / 2);
  10959. }
  10960. }
  10961. // check the edges
  10962. if (horiz) {
  10963. limit = {
  10964. left: left - crossBox.x,
  10965. right: left + this.width - crossBox.x
  10966. };
  10967. }
  10968. else {
  10969. limit = {
  10970. left: this.labelAlign === 'left' ? left : 0,
  10971. right: this.labelAlign === 'right' ?
  10972. left + this.width :
  10973. chart.chartWidth
  10974. };
  10975. }
  10976. // left edge
  10977. if (crossLabel.translateX < limit.left) {
  10978. offset = limit.left - crossLabel.translateX;
  10979. }
  10980. // right edge
  10981. if (crossLabel.translateX + crossBox.width >= limit.right) {
  10982. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  10983. }
  10984. // show the crosslabel
  10985. crossLabel.attr({
  10986. x: posx + offset,
  10987. y: posy,
  10988. // First set x and y, then anchorX and anchorY, when box is actually
  10989. // calculated, #5702
  10990. anchorX: horiz ?
  10991. posx :
  10992. (this.opposite ? 0 : chart.chartWidth),
  10993. anchorY: horiz ?
  10994. (this.opposite ? chart.chartHeight : 0) :
  10995. posy + crossBox.height / 2
  10996. });
  10997. });
  10998. /* ************************************************************************** *
  10999. * Start value compare logic *
  11000. * ************************************************************************** */
  11001. /**
  11002. * Extend series.init by adding a method to modify the y value used for plotting
  11003. * on the y axis. This method is called both from the axis when finding dataMin
  11004. * and dataMax, and from the series.translate method.
  11005. *
  11006. * @ignore
  11007. * @function Highcharts.Series#init
  11008. */
  11009. Series.prototype.init = function () {
  11010. // Call base method
  11011. seriesInit.apply(this, arguments);
  11012. // Set comparison mode
  11013. this.initCompare(this.options.compare);
  11014. };
  11015. /**
  11016. * Highcharts Stock only. Set the
  11017. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  11018. * mode of the series after render time. In most cases it is more useful running
  11019. * {@link Axis#setCompare} on the X axis to update all its series.
  11020. *
  11021. * @function Highcharts.Series#setCompare
  11022. *
  11023. * @param {string} [compare]
  11024. * Can be one of `null` (default), `"percent"` or `"value"`.
  11025. */
  11026. Series.prototype.setCompare = function (compare) {
  11027. this.initCompare(compare);
  11028. // Survive to export, #5485
  11029. this.userOptions.compare = compare;
  11030. };
  11031. /**
  11032. * @ignore
  11033. * @function Highcharts.Series#initCompare
  11034. *
  11035. * @param {string} [compare]
  11036. * Can be one of `null` (default), `"percent"` or `"value"`.
  11037. */
  11038. Series.prototype.initCompare = function (compare) {
  11039. // Set or unset the modifyValue method
  11040. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  11041. function (value, point) {
  11042. var compareValue = this.compareValue;
  11043. if (typeof value !== 'undefined' &&
  11044. typeof compareValue !== 'undefined') { // #2601, #5814
  11045. // Get the modified value
  11046. if (compare === 'value') {
  11047. value -= compareValue;
  11048. // Compare percent
  11049. }
  11050. else {
  11051. value = 100 * (value / compareValue) -
  11052. (this.options.compareBase === 100 ? 0 : 100);
  11053. }
  11054. // record for tooltip etc.
  11055. if (point) {
  11056. point.change = value;
  11057. }
  11058. return value;
  11059. }
  11060. return 0;
  11061. } :
  11062. null;
  11063. // Mark dirty
  11064. if (this.chart.hasRendered) {
  11065. this.isDirty = true;
  11066. }
  11067. };
  11068. /**
  11069. * Extend series.processData by finding the first y value in the plot area,
  11070. * used for comparing the following values
  11071. *
  11072. * @ignore
  11073. * @function Highcharts.Series#processData
  11074. */
  11075. Series.prototype.processData = function (force) {
  11076. var series = this,
  11077. i,
  11078. keyIndex = -1,
  11079. processedXData,
  11080. processedYData,
  11081. compareStart = series.options.compareStart === true ? 0 : 1,
  11082. length,
  11083. compareValue;
  11084. // call base method
  11085. seriesProcessData.apply(this, arguments);
  11086. if (series.xAxis && series.processedYData) { // not pies
  11087. // local variables
  11088. processedXData = series.processedXData;
  11089. processedYData = series.processedYData;
  11090. length = processedYData.length;
  11091. // For series with more than one value (range, OHLC etc), compare
  11092. // against close or the pointValKey (#4922, #3112, #9854)
  11093. if (series.pointArrayMap) {
  11094. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  11095. }
  11096. // find the first value for comparison
  11097. for (i = 0; i < length - compareStart; i++) {
  11098. compareValue = processedYData[i] && keyIndex > -1 ?
  11099. processedYData[i][keyIndex] :
  11100. processedYData[i];
  11101. if (isNumber(compareValue) &&
  11102. processedXData[i + compareStart] >=
  11103. series.xAxis.min &&
  11104. compareValue !== 0) {
  11105. series.compareValue = compareValue;
  11106. break;
  11107. }
  11108. }
  11109. }
  11110. return;
  11111. };
  11112. // Modify series extremes
  11113. addEvent(Series, 'afterGetExtremes', function (e) {
  11114. var dataExtremes = e.dataExtremes;
  11115. if (this.modifyValue && dataExtremes) {
  11116. var extremes = [
  11117. this.modifyValue(dataExtremes.dataMin),
  11118. this.modifyValue(dataExtremes.dataMax)
  11119. ];
  11120. dataExtremes.dataMin = arrayMin(extremes);
  11121. dataExtremes.dataMax = arrayMax(extremes);
  11122. }
  11123. });
  11124. /**
  11125. * Highcharts Stock only. Set the compare mode on all series
  11126. * belonging to an Y axis after render time.
  11127. *
  11128. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  11129. *
  11130. * @sample stock/members/axis-setcompare/
  11131. * Set compoare
  11132. *
  11133. * @function Highcharts.Axis#setCompare
  11134. *
  11135. * @param {string} [compare]
  11136. * The compare mode. Can be one of `null` (default), `"value"` or
  11137. * `"percent"`.
  11138. *
  11139. * @param {boolean} [redraw=true]
  11140. * Whether to redraw the chart or to wait for a later call to
  11141. * {@link Chart#redraw}.
  11142. */
  11143. Axis.prototype.setCompare = function (compare, redraw) {
  11144. if (!this.isXAxis) {
  11145. this.series.forEach(function (series) {
  11146. series.setCompare(compare);
  11147. });
  11148. if (pick(redraw, true)) {
  11149. this.chart.redraw();
  11150. }
  11151. }
  11152. };
  11153. /**
  11154. * Extend the tooltip formatter by adding support for the point.change variable
  11155. * as well as the changeDecimals option.
  11156. *
  11157. * @ignore
  11158. * @function Highcharts.Point#tooltipFormatter
  11159. *
  11160. * @param {string} pointFormat
  11161. */
  11162. Point.prototype.tooltipFormatter = function (pointFormat) {
  11163. var point = this;
  11164. var numberFormatter = point.series.chart.numberFormatter;
  11165. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  11166. return pointTooltipFormatter.apply(this, [pointFormat]);
  11167. };
  11168. /* ************************************************************************** *
  11169. * End value compare logic *
  11170. * ************************************************************************** */
  11171. // Extend the Series prototype to create a separate series clip box. This is
  11172. // related to using multiple panes, and a future pane logic should incorporate
  11173. // this feature (#2754).
  11174. addEvent(Series, 'render', function () {
  11175. var chart = this.chart,
  11176. clipHeight;
  11177. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  11178. // if the series type handles clipping in the animate method (#2975).
  11179. if (!(chart.is3d && chart.is3d()) &&
  11180. !chart.polar &&
  11181. this.xAxis &&
  11182. !this.xAxis.isRadial && // Gauge, #6192
  11183. this.options.clip !== false // #15128
  11184. ) {
  11185. clipHeight = this.yAxis.len;
  11186. // Include xAxis line width (#8031) but only if the Y axis ends on the
  11187. // edge of the X axis (#11005).
  11188. if (this.xAxis.axisLine) {
  11189. var dist = chart.plotTop + chart.plotHeight -
  11190. this.yAxis.pos - this.yAxis.len,
  11191. lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  11192. if (dist >= 0) {
  11193. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  11194. }
  11195. }
  11196. // First render, initial clip box. clipBox also needs to be updated if
  11197. // the series is rendered again before starting animating, in
  11198. // compliance with a responsive rule (#13858).
  11199. if (!chart.hasLoaded || (!this.clipBox && this.isDirty && !this.isDirtyData)) {
  11200. this.clipBox = this.clipBox || merge(chart.clipBox);
  11201. this.clipBox.width = this.xAxis.len;
  11202. this.clipBox.height = clipHeight;
  11203. }
  11204. if (chart.hasRendered) {
  11205. var animation = animObject(this.options.animation);
  11206. // #15435: this.sharedClipKey might not have been set yet, for
  11207. // example when updating the series, so we need to use this
  11208. // function instead
  11209. var sharedClipKey = this.getSharedClipKey(animation);
  11210. var clipRect = chart.sharedClips[sharedClipKey];
  11211. // On redrawing, resizing etc, update the clip rectangle.
  11212. //
  11213. // #15435: Update it even when we are creating/updating clipBox,
  11214. // since there could be series updating and pane size changes
  11215. // happening at the same time and we dont destroy shared clips in
  11216. // stock.
  11217. if (clipRect) {
  11218. // animate in case resize is done during initial animation
  11219. clipRect.animate({
  11220. width: this.xAxis.len,
  11221. height: clipHeight
  11222. });
  11223. var markerClipRect = chart.sharedClips[sharedClipKey + 'm'];
  11224. // also change markers clip animation for consistency
  11225. // (marker clip rects should exist only on chart init)
  11226. if (markerClipRect) {
  11227. markerClipRect.animate({
  11228. width: this.xAxis.len
  11229. });
  11230. }
  11231. }
  11232. }
  11233. }
  11234. });
  11235. addEvent(Chart, 'update', function (e) {
  11236. var options = e.options;
  11237. // Use case: enabling scrollbar from a disabled state.
  11238. // Scrollbar needs to be initialized from a controller, Navigator in this
  11239. // case (#6615)
  11240. if ('scrollbar' in options && this.navigator) {
  11241. merge(true, this.options.scrollbar, options.scrollbar);
  11242. this.navigator.update({}, false);
  11243. delete options.scrollbar;
  11244. }
  11245. });
  11246. /* *
  11247. *
  11248. * Default Export
  11249. *
  11250. * */
  11251. /* *
  11252. *
  11253. * API Options
  11254. *
  11255. * */
  11256. /**
  11257. * Compare the values of the series against the first non-null, non-
  11258. * zero value in the visible range. The y axis will show percentage
  11259. * or absolute change depending on whether `compare` is set to `"percent"`
  11260. * or `"value"`. When this is applied to multiple series, it allows
  11261. * comparing the development of the series against each other. Adds
  11262. * a `change` field to every point object.
  11263. *
  11264. * @see [compareBase](#plotOptions.series.compareBase)
  11265. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  11266. *
  11267. * @sample {highstock} stock/plotoptions/series-compare-percent/
  11268. * Percent
  11269. * @sample {highstock} stock/plotoptions/series-compare-value/
  11270. * Value
  11271. *
  11272. * @type {string}
  11273. * @since 1.0.1
  11274. * @product highstock
  11275. * @apioption plotOptions.series.compare
  11276. */
  11277. /**
  11278. * Defines if comparison should start from the first point within the visible
  11279. * range or should start from the first point **before** the range.
  11280. *
  11281. * In other words, this flag determines if first point within the visible range
  11282. * will have 0% (`compareStart=true`) or should have been already calculated
  11283. * according to the previous point (`compareStart=false`).
  11284. *
  11285. * @sample {highstock} stock/plotoptions/series-comparestart/
  11286. * Calculate compare within visible range
  11287. *
  11288. * @type {boolean}
  11289. * @default false
  11290. * @since 6.0.0
  11291. * @product highstock
  11292. * @apioption plotOptions.series.compareStart
  11293. */
  11294. /**
  11295. * When [compare](#plotOptions.series.compare) is `percent`, this option
  11296. * dictates whether to use 0 or 100 as the base of comparison.
  11297. *
  11298. * @sample {highstock} stock/plotoptions/series-comparebase/
  11299. * Compare base is 100
  11300. *
  11301. * @type {number}
  11302. * @default 0
  11303. * @since 5.0.6
  11304. * @product highstock
  11305. * @validvalue [0, 100]
  11306. * @apioption plotOptions.series.compareBase
  11307. */
  11308. ''; // keeps doclets above in transpiled file
  11309. return StockChart;
  11310. });
  11311. _registerModule(_modules, 'masters/modules/stock.src.js', [_modules['Core/Globals.js'], _modules['Core/Scrollbar.js'], _modules['Core/Chart/StockChart.js']], function (Highcharts, Scrollbar, StockChart) {
  11312. var G = Highcharts;
  11313. // Classes
  11314. G.Scrollbar = Scrollbar;
  11315. G.StockChart = G.stockChart = StockChart.stockChart;
  11316. // Compositions
  11317. Scrollbar.compose(G.Axis);
  11318. });
  11319. }));