stock.src.js 478 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827
  1. /**
  2. * @license Highstock JS v9.0.1 (2021-02-16)
  3. *
  4. * Highstock 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/Globals.js'], _modules['Core/Utilities.js']], function (H, 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. /* eslint-disable no-invalid-this, valid-jsdoc */
  220. /**
  221. * Creates scrollbars if enabled.
  222. *
  223. * @private
  224. */
  225. var ScrollbarAxis = /** @class */ (function () {
  226. function ScrollbarAxis() {
  227. }
  228. /**
  229. * Attaches to axis events to create scrollbars if enabled.
  230. *
  231. * @private
  232. *
  233. * @param AxisClass
  234. * Axis class to extend.
  235. *
  236. * @param ScrollbarClass
  237. * Scrollbar class to use.
  238. */
  239. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  240. var getExtremes = function (axis) {
  241. var axisMin = pick(axis.options && axis.options.min, axis.min);
  242. var axisMax = pick(axis.options && axis.options.max,
  243. axis.max);
  244. return {
  245. axisMin: axisMin,
  246. axisMax: axisMax,
  247. scrollMin: defined(axis.dataMin) ?
  248. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  249. scrollMax: defined(axis.dataMax) ?
  250. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  251. };
  252. };
  253. // Wrap axis initialization and create scrollbar if enabled:
  254. addEvent(AxisClass, 'afterInit', function () {
  255. var axis = this;
  256. if (axis.options &&
  257. axis.options.scrollbar &&
  258. axis.options.scrollbar.enabled) {
  259. // Predefined options:
  260. axis.options.scrollbar.vertical = !axis.horiz;
  261. axis.options.startOnTick = axis.options.endOnTick = false;
  262. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  263. addEvent(axis.scrollbar, 'changed', function (e) {
  264. var _a = getExtremes(axis),
  265. axisMin = _a.axisMin,
  266. axisMax = _a.axisMax,
  267. unitedMin = _a.scrollMin,
  268. unitedMax = _a.scrollMax,
  269. range = unitedMax - unitedMin,
  270. to,
  271. from;
  272. // #12834, scroll when show/hide series, wrong extremes
  273. if (!defined(axisMin) || !defined(axisMax)) {
  274. return;
  275. }
  276. if ((axis.horiz && !axis.reversed) ||
  277. (!axis.horiz && axis.reversed)) {
  278. to = unitedMin + range * this.to;
  279. from = unitedMin + range * this.from;
  280. }
  281. else {
  282. // y-values in browser are reversed, but this also
  283. // applies for reversed horizontal axis:
  284. to = unitedMin + range * (1 - this.from);
  285. from = unitedMin + range * (1 - this.to);
  286. }
  287. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  288. // Mouseup always should change extremes
  289. e.DOMType === 'mouseup' ||
  290. e.DOMType === 'touchend' ||
  291. // Internal events
  292. !defined(e.DOMType)) {
  293. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e);
  294. }
  295. else {
  296. // When live redraw is disabled, don't change extremes
  297. // Only change the position of the scollbar thumb
  298. this.setRange(this.from, this.to);
  299. }
  300. });
  301. }
  302. });
  303. // Wrap rendering axis, and update scrollbar if one is created:
  304. addEvent(AxisClass, 'afterRender', function () {
  305. var axis = this,
  306. _a = getExtremes(axis),
  307. scrollMin = _a.scrollMin,
  308. scrollMax = _a.scrollMax,
  309. scrollbar = axis.scrollbar,
  310. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  311. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  312. axisMargin = axis.options.margin || 0,
  313. offsetsIndex,
  314. from,
  315. to;
  316. if (scrollbar) {
  317. if (axis.horiz) {
  318. // Reserve space for labels/title
  319. if (!axis.opposite) {
  320. scrollbarsOffsets[1] += offset;
  321. }
  322. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  323. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  324. // Next scrollbar should reserve space for margin (if set)
  325. if (!axis.opposite) {
  326. scrollbarsOffsets[1] += axisMargin;
  327. }
  328. offsetsIndex = 1;
  329. }
  330. else {
  331. // Reserve space for labels/title
  332. if (axis.opposite) {
  333. scrollbarsOffsets[0] += offset;
  334. }
  335. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  336. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  337. // Next scrollbar should reserve space for margin (if set)
  338. if (axis.opposite) {
  339. scrollbarsOffsets[0] += axisMargin;
  340. }
  341. offsetsIndex = 0;
  342. }
  343. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  344. scrollbar.options.margin;
  345. if (isNaN(scrollMin) ||
  346. isNaN(scrollMax) ||
  347. !defined(axis.min) ||
  348. !defined(axis.max) ||
  349. axis.min === axis.max // #10733
  350. ) {
  351. // default action: when extremes are the same or there is
  352. // not extremes on the axis, but scrollbar exists, make it
  353. // full size
  354. scrollbar.setRange(0, 1);
  355. }
  356. else {
  357. from =
  358. (axis.min - scrollMin) / (scrollMax - scrollMin);
  359. to =
  360. (axis.max - scrollMin) / (scrollMax - scrollMin);
  361. if ((axis.horiz && !axis.reversed) ||
  362. (!axis.horiz && axis.reversed)) {
  363. scrollbar.setRange(from, to);
  364. }
  365. else {
  366. // inverse vertical axis
  367. scrollbar.setRange(1 - to, 1 - from);
  368. }
  369. }
  370. }
  371. });
  372. // Make space for a scrollbar:
  373. addEvent(AxisClass, 'afterGetOffset', function () {
  374. var axis = this,
  375. index = axis.horiz ? 2 : 1,
  376. scrollbar = axis.scrollbar;
  377. if (scrollbar) {
  378. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  379. axis.chart.axisOffset[index] +=
  380. scrollbar.size + scrollbar.options.margin;
  381. }
  382. });
  383. };
  384. return ScrollbarAxis;
  385. }());
  386. return ScrollbarAxis;
  387. });
  388. _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palette.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Axis, H, palette, ScrollbarAxis, U, O) {
  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 addEvent = U.addEvent,
  399. correctFloat = U.correctFloat,
  400. defined = U.defined,
  401. destroyObjectProperties = U.destroyObjectProperties,
  402. fireEvent = U.fireEvent,
  403. merge = U.merge,
  404. pick = U.pick,
  405. removeEvent = U.removeEvent;
  406. var defaultOptions = O.defaultOptions;
  407. var isTouchDevice = H.isTouchDevice;
  408. /**
  409. * When we have vertical scrollbar, rifles and arrow in buttons should be
  410. * rotated. The same method is used in Navigator's handles, to rotate them.
  411. *
  412. * @function Highcharts.swapXY
  413. *
  414. * @param {Highcharts.SVGPathArray} path
  415. * Path to be rotated.
  416. *
  417. * @param {boolean} [vertical]
  418. * If vertical scrollbar, swap x-y values.
  419. *
  420. * @return {Highcharts.SVGPathArray}
  421. * Rotated path.
  422. *
  423. * @requires modules/stock
  424. */
  425. var swapXY = H.swapXY = function (path,
  426. vertical) {
  427. if (vertical) {
  428. path.forEach(function (seg) {
  429. var len = seg.length;
  430. var temp;
  431. for (var i = 0; i < len; i += 2) {
  432. temp = seg[i + 1];
  433. if (typeof temp === 'number') {
  434. seg[i + 1] = seg[i + 2];
  435. seg[i + 2] = temp;
  436. }
  437. }
  438. });
  439. }
  440. return path;
  441. };
  442. /* eslint-disable no-invalid-this, valid-jsdoc */
  443. /**
  444. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  445. * on individual axes.
  446. *
  447. * @private
  448. * @class
  449. * @name Highcharts.Scrollbar
  450. * @param {Highcharts.SVGRenderer} renderer
  451. * @param {Highcharts.ScrollbarOptions} options
  452. * @param {Highcharts.Chart} chart
  453. */
  454. var Scrollbar = /** @class */ (function () {
  455. /* *
  456. *
  457. * Constructors
  458. *
  459. * */
  460. function Scrollbar(renderer, options, chart) {
  461. /* *
  462. *
  463. * Properties
  464. *
  465. * */
  466. this._events = [];
  467. this.chartX = 0;
  468. this.chartY = 0;
  469. this.from = 0;
  470. this.group = void 0;
  471. this.scrollbar = void 0;
  472. this.scrollbarButtons = [];
  473. this.scrollbarGroup = void 0;
  474. this.scrollbarLeft = 0;
  475. this.scrollbarRifles = void 0;
  476. this.scrollbarStrokeWidth = 1;
  477. this.scrollbarTop = 0;
  478. this.size = 0;
  479. this.to = 0;
  480. this.track = void 0;
  481. this.trackBorderWidth = 1;
  482. this.userOptions = {};
  483. this.x = 0;
  484. this.y = 0;
  485. this.chart = chart;
  486. this.options = options;
  487. this.renderer = chart.renderer;
  488. this.init(renderer, options, chart);
  489. }
  490. /* *
  491. *
  492. * Functions
  493. *
  494. * */
  495. /**
  496. * Set up the mouse and touch events for the Scrollbar
  497. *
  498. * @private
  499. * @function Highcharts.Scrollbar#addEvents
  500. * @return {void}
  501. */
  502. Scrollbar.prototype.addEvents = function () {
  503. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  504. buttons = this.scrollbarButtons,
  505. bar = this.scrollbarGroup.element,
  506. track = this.track.element,
  507. mouseDownHandler = this.mouseDownHandler.bind(this),
  508. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  509. mouseUpHandler = this.mouseUpHandler.bind(this),
  510. _events;
  511. // Mouse events
  512. _events = [
  513. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  514. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  515. [track, 'click', this.trackClick.bind(this)],
  516. [bar, 'mousedown', mouseDownHandler],
  517. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  518. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  519. ];
  520. // Touch events
  521. if (H.hasTouch) {
  522. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  523. }
  524. // Add them all
  525. _events.forEach(function (args) {
  526. addEvent.apply(null, args);
  527. });
  528. this._events = _events;
  529. };
  530. Scrollbar.prototype.buttonToMaxClick = function (e) {
  531. var scroller = this;
  532. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  533. scroller.updatePosition(scroller.from + range, scroller.to + range);
  534. fireEvent(scroller, 'changed', {
  535. from: scroller.from,
  536. to: scroller.to,
  537. trigger: 'scrollbar',
  538. DOMEvent: e
  539. });
  540. };
  541. Scrollbar.prototype.buttonToMinClick = function (e) {
  542. var scroller = this;
  543. var range = correctFloat(scroller.to - scroller.from) *
  544. pick(scroller.options.step, 0.2);
  545. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  546. fireEvent(scroller, 'changed', {
  547. from: scroller.from,
  548. to: scroller.to,
  549. trigger: 'scrollbar',
  550. DOMEvent: e
  551. });
  552. };
  553. /**
  554. * Get normalized (0-1) cursor position over the scrollbar
  555. *
  556. * @private
  557. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  558. *
  559. * @param {*} normalizedEvent
  560. * normalized event, with chartX and chartY values
  561. *
  562. * @return {Highcharts.Dictionary<number>}
  563. * Local position {chartX, chartY}
  564. */
  565. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  566. var scroller = this,
  567. options = scroller.options,
  568. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  569. options.minWidth :
  570. 0; // minWidth distorts translation
  571. return {
  572. chartX: (normalizedEvent.chartX - scroller.x -
  573. scroller.xOffset) /
  574. (scroller.barWidth - minWidthDifference),
  575. chartY: (normalizedEvent.chartY - scroller.y -
  576. scroller.yOffset) /
  577. (scroller.barWidth - minWidthDifference)
  578. };
  579. };
  580. /**
  581. * Destroys allocated elements.
  582. *
  583. * @private
  584. * @function Highcharts.Scrollbar#destroy
  585. * @return {void}
  586. */
  587. Scrollbar.prototype.destroy = function () {
  588. var scroller = this.chart.scroller;
  589. // Disconnect events added in addEvents
  590. this.removeEvents();
  591. // Destroy properties
  592. [
  593. 'track',
  594. 'scrollbarRifles',
  595. 'scrollbar',
  596. 'scrollbarGroup',
  597. 'group'
  598. ].forEach(function (prop) {
  599. if (this[prop] && this[prop].destroy) {
  600. this[prop] = this[prop].destroy();
  601. }
  602. }, this);
  603. // #6421, chart may have more scrollbars
  604. if (scroller && this === scroller.scrollbar) {
  605. scroller.scrollbar = null;
  606. // Destroy elements in collection
  607. destroyObjectProperties(scroller.scrollbarButtons);
  608. }
  609. };
  610. /**
  611. * Draw the scrollbar buttons with arrows
  612. *
  613. * @private
  614. * @function Highcharts.Scrollbar#drawScrollbarButton
  615. * @param {number} index
  616. * 0 is left, 1 is right
  617. * @return {void}
  618. */
  619. Scrollbar.prototype.drawScrollbarButton = function (index) {
  620. var scroller = this,
  621. renderer = scroller.renderer,
  622. scrollbarButtons = scroller.scrollbarButtons,
  623. options = scroller.options,
  624. size = scroller.size,
  625. group,
  626. tempElem;
  627. group = renderer.g().add(scroller.group);
  628. scrollbarButtons.push(group);
  629. // Create a rectangle for the scrollbar button
  630. tempElem = renderer.rect()
  631. .addClass('highcharts-scrollbar-button')
  632. .add(group);
  633. // Presentational attributes
  634. if (!this.chart.styledMode) {
  635. tempElem.attr({
  636. stroke: options.buttonBorderColor,
  637. 'stroke-width': options.buttonBorderWidth,
  638. fill: options.buttonBackgroundColor
  639. });
  640. }
  641. // Place the rectangle based on the rendered stroke width
  642. tempElem.attr(tempElem.crisp({
  643. x: -0.5,
  644. y: -0.5,
  645. width: size + 1,
  646. height: size + 1,
  647. r: options.buttonBorderRadius
  648. }, tempElem.strokeWidth()));
  649. // Button arrow
  650. tempElem = renderer
  651. .path(swapXY([[
  652. 'M',
  653. size / 2 + (index ? -1 : 1),
  654. size / 2 - 3
  655. ], [
  656. 'L',
  657. size / 2 + (index ? -1 : 1),
  658. size / 2 + 3
  659. ], [
  660. 'L',
  661. size / 2 + (index ? 2 : -2),
  662. size / 2
  663. ]], options.vertical))
  664. .addClass('highcharts-scrollbar-arrow')
  665. .add(scrollbarButtons[index]);
  666. if (!this.chart.styledMode) {
  667. tempElem.attr({
  668. fill: options.buttonArrowColor
  669. });
  670. }
  671. };
  672. /**
  673. * @private
  674. * @function Highcharts.Scrollbar#init
  675. * @param {Highcharts.SVGRenderer} renderer
  676. * @param {Highcharts.ScrollbarOptions} options
  677. * @param {Highcharts.Chart} chart
  678. */
  679. Scrollbar.prototype.init = function (renderer, options, chart) {
  680. this.scrollbarButtons = [];
  681. this.renderer = renderer;
  682. this.userOptions = options;
  683. this.options = merge(Scrollbar.defaultOptions, options);
  684. this.chart = chart;
  685. // backward compatibility
  686. this.size = pick(this.options.size, this.options.height);
  687. // Init
  688. if (options.enabled) {
  689. this.render();
  690. this.addEvents();
  691. }
  692. };
  693. Scrollbar.prototype.mouseDownHandler = function (e) {
  694. var scroller = this;
  695. var normalizedEvent = scroller.chart.pointer.normalize(e),
  696. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  697. scroller.chartX = mousePosition.chartX;
  698. scroller.chartY = mousePosition.chartY;
  699. scroller.initPositions = [scroller.from, scroller.to];
  700. scroller.grabbedCenter = true;
  701. };
  702. /**
  703. * Event handler for the mouse move event.
  704. * @private
  705. */
  706. Scrollbar.prototype.mouseMoveHandler = function (e) {
  707. var scroller = this;
  708. var normalizedEvent = scroller.chart.pointer.normalize(e),
  709. options = scroller.options,
  710. direction = options.vertical ? 'chartY' : 'chartX',
  711. initPositions = scroller.initPositions || [],
  712. scrollPosition,
  713. chartPosition,
  714. change;
  715. // In iOS, a mousemove event with e.pageX === 0 is fired when
  716. // holding the finger down in the center of the scrollbar. This
  717. // should be ignored.
  718. if (scroller.grabbedCenter &&
  719. // #4696, scrollbar failed on Android
  720. (!e.touches || e.touches[0][direction] !== 0)) {
  721. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  722. scrollPosition = scroller[direction];
  723. change = chartPosition - scrollPosition;
  724. scroller.hasDragged = true;
  725. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  726. if (scroller.hasDragged) {
  727. fireEvent(scroller, 'changed', {
  728. from: scroller.from,
  729. to: scroller.to,
  730. trigger: 'scrollbar',
  731. DOMType: e.type,
  732. DOMEvent: e
  733. });
  734. }
  735. }
  736. };
  737. /**
  738. * Event handler for the mouse up event.
  739. * @private
  740. */
  741. Scrollbar.prototype.mouseUpHandler = function (e) {
  742. var scroller = this;
  743. if (scroller.hasDragged) {
  744. fireEvent(scroller, 'changed', {
  745. from: scroller.from,
  746. to: scroller.to,
  747. trigger: 'scrollbar',
  748. DOMType: e.type,
  749. DOMEvent: e
  750. });
  751. }
  752. scroller.grabbedCenter =
  753. scroller.hasDragged =
  754. scroller.chartX =
  755. scroller.chartY = null;
  756. };
  757. /**
  758. * Position the scrollbar, method called from a parent with defined
  759. * dimensions.
  760. *
  761. * @private
  762. * @function Highcharts.Scrollbar#position
  763. * @param {number} x
  764. * x-position on the chart
  765. * @param {number} y
  766. * y-position on the chart
  767. * @param {number} width
  768. * width of the scrollbar
  769. * @param {number} height
  770. * height of the scorllbar
  771. * @return {void}
  772. */
  773. Scrollbar.prototype.position = function (x, y, width, height) {
  774. var scroller = this,
  775. options = scroller.options,
  776. vertical = options.vertical,
  777. xOffset = height,
  778. yOffset = 0,
  779. method = scroller.rendered ? 'animate' : 'attr';
  780. scroller.x = x;
  781. scroller.y = y + this.trackBorderWidth;
  782. scroller.width = width; // width with buttons
  783. scroller.height = height;
  784. scroller.xOffset = xOffset;
  785. scroller.yOffset = yOffset;
  786. // If Scrollbar is a vertical type, swap options:
  787. if (vertical) {
  788. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  789. scroller.xOffset = xOffset = 0;
  790. scroller.barWidth = height - width * 2; // width without buttons
  791. scroller.x = x = x + scroller.options.margin;
  792. }
  793. else {
  794. scroller.height = scroller.xOffset = height = xOffset =
  795. scroller.size;
  796. scroller.barWidth = width - height * 2; // width without buttons
  797. scroller.y = scroller.y + scroller.options.margin;
  798. }
  799. // Set general position for a group:
  800. scroller.group[method]({
  801. translateX: x,
  802. translateY: scroller.y
  803. });
  804. // Resize background/track:
  805. scroller.track[method]({
  806. width: width,
  807. height: height
  808. });
  809. // Move right/bottom button ot it's place:
  810. scroller.scrollbarButtons[1][method]({
  811. translateX: vertical ? 0 : width - xOffset,
  812. translateY: vertical ? height - yOffset : 0
  813. });
  814. };
  815. /**
  816. * Removes the event handlers attached previously with addEvents.
  817. *
  818. * @private
  819. * @function Highcharts.Scrollbar#removeEvents
  820. * @return {void}
  821. */
  822. Scrollbar.prototype.removeEvents = function () {
  823. this._events.forEach(function (args) {
  824. removeEvent.apply(null, args);
  825. });
  826. this._events.length = 0;
  827. };
  828. /**
  829. * Render scrollbar with all required items.
  830. *
  831. * @private
  832. * @function Highcharts.Scrollbar#render
  833. */
  834. Scrollbar.prototype.render = function () {
  835. var scroller = this,
  836. renderer = scroller.renderer,
  837. options = scroller.options,
  838. size = scroller.size,
  839. styledMode = this.chart.styledMode,
  840. group;
  841. // Draw the scrollbar group
  842. scroller.group = group = renderer.g('scrollbar').attr({
  843. zIndex: options.zIndex,
  844. translateY: -99999
  845. }).add();
  846. // Draw the scrollbar track:
  847. scroller.track = renderer.rect()
  848. .addClass('highcharts-scrollbar-track')
  849. .attr({
  850. x: 0,
  851. r: options.trackBorderRadius || 0,
  852. height: size,
  853. width: size
  854. }).add(group);
  855. if (!styledMode) {
  856. scroller.track.attr({
  857. fill: options.trackBackgroundColor,
  858. stroke: options.trackBorderColor,
  859. 'stroke-width': options.trackBorderWidth
  860. });
  861. }
  862. this.trackBorderWidth = scroller.track.strokeWidth();
  863. scroller.track.attr({
  864. y: -this.trackBorderWidth % 2 / 2
  865. });
  866. // Draw the scrollbar itself
  867. scroller.scrollbarGroup = renderer.g().add(group);
  868. scroller.scrollbar = renderer.rect()
  869. .addClass('highcharts-scrollbar-thumb')
  870. .attr({
  871. height: size,
  872. width: size,
  873. r: options.barBorderRadius || 0
  874. }).add(scroller.scrollbarGroup);
  875. scroller.scrollbarRifles = renderer
  876. .path(swapXY([
  877. ['M', -3, size / 4],
  878. ['L', -3, 2 * size / 3],
  879. ['M', 0, size / 4],
  880. ['L', 0, 2 * size / 3],
  881. ['M', 3, size / 4],
  882. ['L', 3, 2 * size / 3]
  883. ], options.vertical))
  884. .addClass('highcharts-scrollbar-rifles')
  885. .add(scroller.scrollbarGroup);
  886. if (!styledMode) {
  887. scroller.scrollbar.attr({
  888. fill: options.barBackgroundColor,
  889. stroke: options.barBorderColor,
  890. 'stroke-width': options.barBorderWidth
  891. });
  892. scroller.scrollbarRifles.attr({
  893. stroke: options.rifleColor,
  894. 'stroke-width': 1
  895. });
  896. }
  897. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  898. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  899. // Draw the buttons:
  900. scroller.drawScrollbarButton(0);
  901. scroller.drawScrollbarButton(1);
  902. };
  903. /**
  904. * Set scrollbar size, with a given scale.
  905. *
  906. * @private
  907. * @function Highcharts.Scrollbar#setRange
  908. * @param {number} from
  909. * scale (0-1) where bar should start
  910. * @param {number} to
  911. * scale (0-1) where bar should end
  912. * @return {void}
  913. */
  914. Scrollbar.prototype.setRange = function (from, to) {
  915. var scroller = this,
  916. options = scroller.options,
  917. vertical = options.vertical,
  918. minWidth = options.minWidth,
  919. fullWidth = scroller.barWidth,
  920. fromPX,
  921. toPX,
  922. newPos,
  923. newSize,
  924. newRiflesPos,
  925. method = (this.rendered &&
  926. !this.hasDragged &&
  927. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  928. if (!defined(fullWidth)) {
  929. return;
  930. }
  931. from = Math.max(from, 0);
  932. fromPX = Math.ceil(fullWidth * from);
  933. toPX = fullWidth * Math.min(to, 1);
  934. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  935. // We need to recalculate position, if minWidth is used
  936. if (newSize < minWidth) {
  937. fromPX = (fullWidth - minWidth + newSize) * from;
  938. newSize = minWidth;
  939. }
  940. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  941. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  942. // Store current position:
  943. scroller.from = from;
  944. scroller.to = to;
  945. if (!vertical) {
  946. scroller.scrollbarGroup[method]({
  947. translateX: newPos
  948. });
  949. scroller.scrollbar[method]({
  950. width: newSize
  951. });
  952. scroller.scrollbarRifles[method]({
  953. translateX: newRiflesPos
  954. });
  955. scroller.scrollbarLeft = newPos;
  956. scroller.scrollbarTop = 0;
  957. }
  958. else {
  959. scroller.scrollbarGroup[method]({
  960. translateY: newPos
  961. });
  962. scroller.scrollbar[method]({
  963. height: newSize
  964. });
  965. scroller.scrollbarRifles[method]({
  966. translateY: newRiflesPos
  967. });
  968. scroller.scrollbarTop = newPos;
  969. scroller.scrollbarLeft = 0;
  970. }
  971. if (newSize <= 12) {
  972. scroller.scrollbarRifles.hide();
  973. }
  974. else {
  975. scroller.scrollbarRifles.show(true);
  976. }
  977. // Show or hide the scrollbar based on the showFull setting
  978. if (options.showFull === false) {
  979. if (from <= 0 && to >= 1) {
  980. scroller.group.hide();
  981. }
  982. else {
  983. scroller.group.show();
  984. }
  985. }
  986. scroller.rendered = true;
  987. };
  988. Scrollbar.prototype.trackClick = function (e) {
  989. var scroller = this;
  990. var normalizedEvent = scroller.chart.pointer.normalize(e),
  991. range = scroller.to - scroller.from,
  992. top = scroller.y + scroller.scrollbarTop,
  993. left = scroller.x + scroller.scrollbarLeft;
  994. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  995. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  996. // On the top or on the left side of the track:
  997. scroller.updatePosition(scroller.from + range, scroller.to + range);
  998. }
  999. else {
  1000. // On the bottom or the right side of the track:
  1001. scroller.updatePosition(scroller.from - range, scroller.to - range);
  1002. }
  1003. fireEvent(scroller, 'changed', {
  1004. from: scroller.from,
  1005. to: scroller.to,
  1006. trigger: 'scrollbar',
  1007. DOMEvent: e
  1008. });
  1009. };
  1010. /**
  1011. * Update the scrollbar with new options
  1012. *
  1013. * @private
  1014. * @function Highcharts.Scrollbar#update
  1015. * @param {Highcharts.ScrollbarOptions} options
  1016. * @return {void}
  1017. */
  1018. Scrollbar.prototype.update = function (options) {
  1019. this.destroy();
  1020. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  1021. };
  1022. /**
  1023. * Update position option in the Scrollbar, with normalized 0-1 scale
  1024. *
  1025. * @private
  1026. * @function Highcharts.Scrollbar#updatePosition
  1027. * @param {number} from
  1028. * @param {number} to
  1029. * @return {void}
  1030. */
  1031. Scrollbar.prototype.updatePosition = function (from, to) {
  1032. if (to > 1) {
  1033. from = correctFloat(1 - correctFloat(to - from));
  1034. to = 1;
  1035. }
  1036. if (from < 0) {
  1037. to = correctFloat(to - from);
  1038. from = 0;
  1039. }
  1040. this.from = from;
  1041. this.to = to;
  1042. };
  1043. /* *
  1044. *
  1045. * Static Properties
  1046. *
  1047. * */
  1048. /**
  1049. *
  1050. * The scrollbar is a means of panning over the X axis of a stock chart.
  1051. * Scrollbars can also be applied to other types of axes.
  1052. *
  1053. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  1054. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  1055. * is especially suitable for simpler cartesian charts on mobile.
  1056. *
  1057. * In styled mode, all the presentational options for the
  1058. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  1059. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  1060. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  1061. *
  1062. * @sample stock/yaxis/inverted-bar-scrollbar/
  1063. * A scrollbar on a simple bar chart
  1064. *
  1065. * @product highstock gantt
  1066. * @optionparent scrollbar
  1067. *
  1068. * @private
  1069. */
  1070. Scrollbar.defaultOptions = {
  1071. /**
  1072. * The height of the scrollbar. The height also applies to the width
  1073. * of the scroll arrows so that they are always squares. Defaults to
  1074. * 20 for touch devices and 14 for mouse devices.
  1075. *
  1076. * @sample stock/scrollbar/height/
  1077. * A 30px scrollbar
  1078. *
  1079. * @type {number}
  1080. * @default 20/14
  1081. */
  1082. height: isTouchDevice ? 20 : 14,
  1083. /**
  1084. * The border rounding radius of the bar.
  1085. *
  1086. * @sample stock/scrollbar/style/
  1087. * Scrollbar styling
  1088. */
  1089. barBorderRadius: 0,
  1090. /**
  1091. * The corner radius of the scrollbar buttons.
  1092. *
  1093. * @sample stock/scrollbar/style/
  1094. * Scrollbar styling
  1095. */
  1096. buttonBorderRadius: 0,
  1097. /**
  1098. * Enable or disable the scrollbar.
  1099. *
  1100. * @sample stock/scrollbar/enabled/
  1101. * Disable the scrollbar, only use navigator
  1102. *
  1103. * @type {boolean}
  1104. * @default true
  1105. * @apioption scrollbar.enabled
  1106. */
  1107. /**
  1108. * Whether to redraw the main chart as the scrollbar or the navigator
  1109. * zoomed window is moved. Defaults to `true` for modern browsers and
  1110. * `false` for legacy IE browsers as well as mobile devices.
  1111. *
  1112. * @sample stock/scrollbar/liveredraw
  1113. * Setting live redraw to false
  1114. *
  1115. * @type {boolean}
  1116. * @since 1.3
  1117. */
  1118. liveRedraw: void 0,
  1119. /**
  1120. * The margin between the scrollbar and its axis when the scrollbar is
  1121. * applied directly to an axis.
  1122. */
  1123. margin: 10,
  1124. /**
  1125. * The minimum width of the scrollbar.
  1126. *
  1127. * @since 1.2.5
  1128. */
  1129. minWidth: 6,
  1130. /**
  1131. * Whether to show or hide the scrollbar when the scrolled content is
  1132. * zoomed out to it full extent.
  1133. *
  1134. * @type {boolean}
  1135. * @default true
  1136. * @apioption scrollbar.showFull
  1137. */
  1138. step: 0.2,
  1139. /**
  1140. * The z index of the scrollbar group.
  1141. */
  1142. zIndex: 3,
  1143. /**
  1144. * The background color of the scrollbar itself.
  1145. *
  1146. * @sample stock/scrollbar/style/
  1147. * Scrollbar styling
  1148. *
  1149. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1150. */
  1151. barBackgroundColor: palette.neutralColor20,
  1152. /**
  1153. * The width of the bar's border.
  1154. *
  1155. * @sample stock/scrollbar/style/
  1156. * Scrollbar styling
  1157. */
  1158. barBorderWidth: 1,
  1159. /**
  1160. * The color of the scrollbar's border.
  1161. *
  1162. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1163. */
  1164. barBorderColor: palette.neutralColor20,
  1165. /**
  1166. * The color of the small arrow inside the scrollbar buttons.
  1167. *
  1168. * @sample stock/scrollbar/style/
  1169. * Scrollbar styling
  1170. *
  1171. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1172. */
  1173. buttonArrowColor: palette.neutralColor80,
  1174. /**
  1175. * The color of scrollbar buttons.
  1176. *
  1177. * @sample stock/scrollbar/style/
  1178. * Scrollbar styling
  1179. *
  1180. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1181. */
  1182. buttonBackgroundColor: palette.neutralColor10,
  1183. /**
  1184. * The color of the border of the scrollbar buttons.
  1185. *
  1186. * @sample stock/scrollbar/style/
  1187. * Scrollbar styling
  1188. *
  1189. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1190. */
  1191. buttonBorderColor: palette.neutralColor20,
  1192. /**
  1193. * The border width of the scrollbar buttons.
  1194. *
  1195. * @sample stock/scrollbar/style/
  1196. * Scrollbar styling
  1197. */
  1198. buttonBorderWidth: 1,
  1199. /**
  1200. * The color of the small rifles in the middle of the scrollbar.
  1201. *
  1202. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1203. */
  1204. rifleColor: palette.neutralColor80,
  1205. /**
  1206. * The color of the track background.
  1207. *
  1208. * @sample stock/scrollbar/style/
  1209. * Scrollbar styling
  1210. *
  1211. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1212. */
  1213. trackBackgroundColor: palette.neutralColor5,
  1214. /**
  1215. * The color of the border of the scrollbar track.
  1216. *
  1217. * @sample stock/scrollbar/style/
  1218. * Scrollbar styling
  1219. *
  1220. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1221. */
  1222. trackBorderColor: palette.neutralColor5,
  1223. /**
  1224. * The corner radius of the border of the scrollbar track.
  1225. *
  1226. * @sample stock/scrollbar/style/
  1227. * Scrollbar styling
  1228. *
  1229. * @type {number}
  1230. * @default 0
  1231. * @apioption scrollbar.trackBorderRadius
  1232. */
  1233. /**
  1234. * The width of the border of the scrollbar track.
  1235. *
  1236. * @sample stock/scrollbar/style/
  1237. * Scrollbar styling
  1238. */
  1239. trackBorderWidth: 1
  1240. };
  1241. return Scrollbar;
  1242. }());
  1243. if (!H.Scrollbar) {
  1244. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  1245. H.Scrollbar = Scrollbar;
  1246. ScrollbarAxis.compose(Axis, Scrollbar);
  1247. }
  1248. return H.Scrollbar;
  1249. });
  1250. _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/Options.js'], _modules['Core/Color/Palette.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, O, palette, Scrollbar, Series, SeriesRegistry, U) {
  1251. /* *
  1252. *
  1253. * (c) 2010-2021 Torstein Honsi
  1254. *
  1255. * License: www.highcharts.com/license
  1256. *
  1257. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1258. *
  1259. * */
  1260. var color = Color.parse;
  1261. var hasTouch = H.hasTouch,
  1262. isTouchDevice = H.isTouchDevice;
  1263. var defaultOptions = O.defaultOptions;
  1264. var seriesTypes = SeriesRegistry.seriesTypes;
  1265. var addEvent = U.addEvent,
  1266. clamp = U.clamp,
  1267. correctFloat = U.correctFloat,
  1268. defined = U.defined,
  1269. destroyObjectProperties = U.destroyObjectProperties,
  1270. erase = U.erase,
  1271. extend = U.extend,
  1272. find = U.find,
  1273. isArray = U.isArray,
  1274. isNumber = U.isNumber,
  1275. merge = U.merge,
  1276. pick = U.pick,
  1277. removeEvent = U.removeEvent,
  1278. splat = U.splat;
  1279. var defaultSeriesType,
  1280. // Finding the min or max of a set of variables where we don't know if they
  1281. // are defined, is a pattern that is repeated several places in Highcharts.
  1282. // Consider making this a global utility method.
  1283. numExt = function (extreme) {
  1284. var args = [];
  1285. for (var _i = 1; _i < arguments.length; _i++) {
  1286. args[_i - 1] = arguments[_i];
  1287. }
  1288. var numbers = [].filter.call(args,
  1289. isNumber);
  1290. if (numbers.length) {
  1291. return Math[extreme].apply(0, numbers);
  1292. }
  1293. };
  1294. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  1295. 'line' :
  1296. 'areaspline';
  1297. extend(defaultOptions, {
  1298. /**
  1299. * Maximum range which can be set using the navigator's handles.
  1300. * Opposite of [xAxis.minRange](#xAxis.minRange).
  1301. *
  1302. * @sample {highstock} stock/navigator/maxrange/
  1303. * Defined max and min range
  1304. *
  1305. * @type {number}
  1306. * @since 6.0.0
  1307. * @product highstock gantt
  1308. * @apioption xAxis.maxRange
  1309. */
  1310. /**
  1311. * The navigator is a small series below the main series, displaying
  1312. * a view of the entire data set. It provides tools to zoom in and
  1313. * out on parts of the data as well as panning across the dataset.
  1314. *
  1315. * @product highstock gantt
  1316. * @optionparent navigator
  1317. */
  1318. navigator: {
  1319. /**
  1320. * Whether the navigator and scrollbar should adapt to updated data
  1321. * in the base X axis. When loading data async, as in the demo below,
  1322. * this should be `false`. Otherwise new data will trigger navigator
  1323. * redraw, which will cause unwanted looping. In the demo below, the
  1324. * data in the navigator is set only once. On navigating, only the main
  1325. * chart content is updated.
  1326. *
  1327. * @sample {highstock} stock/demo/lazy-loading/
  1328. * Set to false with async data loading
  1329. *
  1330. * @type {boolean}
  1331. * @default true
  1332. * @apioption navigator.adaptToUpdatedData
  1333. */
  1334. /**
  1335. * An integer identifying the index to use for the base series, or a
  1336. * string representing the id of the series.
  1337. *
  1338. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  1339. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  1340. *
  1341. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  1342. *
  1343. * @deprecated
  1344. * @type {number|string}
  1345. * @default 0
  1346. * @apioption navigator.baseSeries
  1347. */
  1348. /**
  1349. * Enable or disable the navigator.
  1350. *
  1351. * @sample {highstock} stock/navigator/enabled/
  1352. * Disable the navigator
  1353. *
  1354. * @type {boolean}
  1355. * @default true
  1356. * @apioption navigator.enabled
  1357. */
  1358. /**
  1359. * When the chart is inverted, whether to draw the navigator on the
  1360. * opposite side.
  1361. *
  1362. * @type {boolean}
  1363. * @default false
  1364. * @since 5.0.8
  1365. * @apioption navigator.opposite
  1366. */
  1367. /**
  1368. * The height of the navigator.
  1369. *
  1370. * @sample {highstock} stock/navigator/height/
  1371. * A higher navigator
  1372. */
  1373. height: 40,
  1374. /**
  1375. * The distance from the nearest element, the X axis or X axis labels.
  1376. *
  1377. * @sample {highstock} stock/navigator/margin/
  1378. * A margin of 2 draws the navigator closer to the X axis labels
  1379. */
  1380. margin: 25,
  1381. /**
  1382. * Whether the mask should be inside the range marking the zoomed
  1383. * range, or outside. In Highstock 1.x it was always `false`.
  1384. *
  1385. * @sample {highstock} stock/navigator/maskinside-false/
  1386. * False, mask outside
  1387. *
  1388. * @since 2.0
  1389. */
  1390. maskInside: true,
  1391. /**
  1392. * Options for the handles for dragging the zoomed area.
  1393. *
  1394. * @sample {highstock} stock/navigator/handles/
  1395. * Colored handles
  1396. */
  1397. handles: {
  1398. /**
  1399. * Width for handles.
  1400. *
  1401. * @sample {highstock} stock/navigator/styled-handles/
  1402. * Styled handles
  1403. *
  1404. * @since 6.0.0
  1405. */
  1406. width: 7,
  1407. /**
  1408. * Height for handles.
  1409. *
  1410. * @sample {highstock} stock/navigator/styled-handles/
  1411. * Styled handles
  1412. *
  1413. * @since 6.0.0
  1414. */
  1415. height: 15,
  1416. /**
  1417. * Array to define shapes of handles. 0-index for left, 1-index for
  1418. * right.
  1419. *
  1420. * Additionally, the URL to a graphic can be given on this form:
  1421. * `url(graphic.png)`. Note that for the image to be applied to
  1422. * exported charts, its URL needs to be accessible by the export
  1423. * server.
  1424. *
  1425. * Custom callbacks for symbol path generation can also be added to
  1426. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  1427. * used by its method name, as shown in the demo.
  1428. *
  1429. * @sample {highstock} stock/navigator/styled-handles/
  1430. * Styled handles
  1431. *
  1432. * @type {Array<string>}
  1433. * @default ["navigator-handle", "navigator-handle"]
  1434. * @since 6.0.0
  1435. */
  1436. symbols: ['navigator-handle', 'navigator-handle'],
  1437. /**
  1438. * Allows to enable/disable handles.
  1439. *
  1440. * @since 6.0.0
  1441. */
  1442. enabled: true,
  1443. /**
  1444. * The width for the handle border and the stripes inside.
  1445. *
  1446. * @sample {highstock} stock/navigator/styled-handles/
  1447. * Styled handles
  1448. *
  1449. * @since 6.0.0
  1450. * @apioption navigator.handles.lineWidth
  1451. */
  1452. lineWidth: 1,
  1453. /**
  1454. * The fill for the handle.
  1455. *
  1456. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1457. */
  1458. backgroundColor: palette.neutralColor5,
  1459. /**
  1460. * The stroke for the handle border and the stripes inside.
  1461. *
  1462. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1463. */
  1464. borderColor: palette.neutralColor40
  1465. },
  1466. /**
  1467. * The color of the mask covering the areas of the navigator series
  1468. * that are currently not visible in the main series. The default
  1469. * color is bluish with an opacity of 0.3 to see the series below.
  1470. *
  1471. * @see In styled mode, the mask is styled with the
  1472. * `.highcharts-navigator-mask` and
  1473. * `.highcharts-navigator-mask-inside` classes.
  1474. *
  1475. * @sample {highstock} stock/navigator/maskfill/
  1476. * Blue, semi transparent mask
  1477. *
  1478. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1479. * @default rgba(102,133,194,0.3)
  1480. */
  1481. maskFill: color(palette.highlightColor60).setOpacity(0.3).get(),
  1482. /**
  1483. * The color of the line marking the currently zoomed area in the
  1484. * navigator.
  1485. *
  1486. * @sample {highstock} stock/navigator/outline/
  1487. * 2px blue outline
  1488. *
  1489. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1490. * @default #cccccc
  1491. */
  1492. outlineColor: palette.neutralColor20,
  1493. /**
  1494. * The width of the line marking the currently zoomed area in the
  1495. * navigator.
  1496. *
  1497. * @see In styled mode, the outline stroke width is set with the
  1498. * `.highcharts-navigator-outline` class.
  1499. *
  1500. * @sample {highstock} stock/navigator/outline/
  1501. * 2px blue outline
  1502. *
  1503. * @type {number}
  1504. */
  1505. outlineWidth: 1,
  1506. /**
  1507. * Options for the navigator series. Available options are the same
  1508. * as any series, documented at [plotOptions](#plotOptions.series)
  1509. * and [series](#series).
  1510. *
  1511. * Unless data is explicitly defined on navigator.series, the data
  1512. * is borrowed from the first series in the chart.
  1513. *
  1514. * Default series options for the navigator series are:
  1515. * ```js
  1516. * series: {
  1517. * type: 'areaspline',
  1518. * fillOpacity: 0.05,
  1519. * dataGrouping: {
  1520. * smoothed: true
  1521. * },
  1522. * lineWidth: 1,
  1523. * marker: {
  1524. * enabled: false
  1525. * }
  1526. * }
  1527. * ```
  1528. *
  1529. * @see In styled mode, the navigator series is styled with the
  1530. * `.highcharts-navigator-series` class.
  1531. *
  1532. * @sample {highstock} stock/navigator/series-data/
  1533. * Using a separate data set for the navigator
  1534. * @sample {highstock} stock/navigator/series/
  1535. * A green navigator series
  1536. *
  1537. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  1538. */
  1539. series: {
  1540. /**
  1541. * The type of the navigator series.
  1542. *
  1543. * Heads up:
  1544. * In column-type navigator, zooming is limited to at least one
  1545. * point with its `pointRange`.
  1546. *
  1547. * @sample {highstock} stock/navigator/column/
  1548. * Column type navigator
  1549. *
  1550. * @type {string}
  1551. * @default {highstock} `areaspline` if defined, otherwise `line`
  1552. * @default {gantt} gantt
  1553. */
  1554. type: defaultSeriesType,
  1555. /**
  1556. * The fill opacity of the navigator series.
  1557. */
  1558. fillOpacity: 0.05,
  1559. /**
  1560. * The pixel line width of the navigator series.
  1561. */
  1562. lineWidth: 1,
  1563. /**
  1564. * @ignore-option
  1565. */
  1566. compare: null,
  1567. /**
  1568. * Unless data is explicitly defined, the data is borrowed from the
  1569. * first series in the chart.
  1570. *
  1571. * @type {Array<number|Array<number|string|null>|object|null>}
  1572. * @product highstock
  1573. * @apioption navigator.series.data
  1574. */
  1575. /**
  1576. * Data grouping options for the navigator series.
  1577. *
  1578. * @extends plotOptions.series.dataGrouping
  1579. */
  1580. dataGrouping: {
  1581. approximation: 'average',
  1582. enabled: true,
  1583. groupPixelWidth: 2,
  1584. smoothed: true,
  1585. // Day and week differs from plotOptions.series.dataGrouping
  1586. units: [
  1587. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  1588. ['second', [1, 2, 5, 10, 15, 30]],
  1589. ['minute', [1, 2, 5, 10, 15, 30]],
  1590. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  1591. ['day', [1, 2, 3, 4]],
  1592. ['week', [1, 2, 3]],
  1593. ['month', [1, 3, 6]],
  1594. ['year', null]
  1595. ]
  1596. },
  1597. /**
  1598. * Data label options for the navigator series. Data labels are
  1599. * disabled by default on the navigator series.
  1600. *
  1601. * @extends plotOptions.series.dataLabels
  1602. */
  1603. dataLabels: {
  1604. enabled: false,
  1605. zIndex: 2 // #1839
  1606. },
  1607. id: 'highcharts-navigator-series',
  1608. className: 'highcharts-navigator-series',
  1609. /**
  1610. * Sets the fill color of the navigator series.
  1611. *
  1612. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1613. * @apioption navigator.series.color
  1614. */
  1615. /**
  1616. * Line color for the navigator series. Allows setting the color
  1617. * while disallowing the default candlestick setting.
  1618. *
  1619. * @type {Highcharts.ColorString|null}
  1620. */
  1621. lineColor: null,
  1622. marker: {
  1623. enabled: false
  1624. },
  1625. /**
  1626. * Since Highstock v8, default value is the same as default
  1627. * `pointRange` defined for a specific type (e.g. `null` for
  1628. * column type).
  1629. *
  1630. * In Highstock version < 8, defaults to 0.
  1631. *
  1632. * @extends plotOptions.series.pointRange
  1633. * @type {number|null}
  1634. * @apioption navigator.series.pointRange
  1635. */
  1636. /**
  1637. * The threshold option. Setting it to 0 will make the default
  1638. * navigator area series draw its area from the 0 value and up.
  1639. *
  1640. * @type {number|null}
  1641. */
  1642. threshold: null
  1643. },
  1644. /**
  1645. * Options for the navigator X axis. Default series options for the
  1646. * navigator xAxis are:
  1647. * ```js
  1648. * xAxis: {
  1649. * tickWidth: 0,
  1650. * lineWidth: 0,
  1651. * gridLineWidth: 1,
  1652. * tickPixelInterval: 200,
  1653. * labels: {
  1654. * align: 'left',
  1655. * style: {
  1656. * color: '#888'
  1657. * },
  1658. * x: 3,
  1659. * y: -4
  1660. * }
  1661. * }
  1662. * ```
  1663. *
  1664. * @extends xAxis
  1665. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  1666. * showEmpty, maxRange
  1667. */
  1668. xAxis: {
  1669. /**
  1670. * Additional range on the right side of the xAxis. Works similar to
  1671. * xAxis.maxPadding, but value is set in milliseconds.
  1672. * Can be set for both, main xAxis and navigator's xAxis.
  1673. *
  1674. * @since 6.0.0
  1675. */
  1676. overscroll: 0,
  1677. className: 'highcharts-navigator-xaxis',
  1678. tickLength: 0,
  1679. lineWidth: 0,
  1680. gridLineColor: palette.neutralColor10,
  1681. gridLineWidth: 1,
  1682. tickPixelInterval: 200,
  1683. labels: {
  1684. align: 'left',
  1685. /**
  1686. * @type {Highcharts.CSSObject}
  1687. */
  1688. style: {
  1689. /** @ignore */
  1690. color: palette.neutralColor40
  1691. },
  1692. x: 3,
  1693. y: -4
  1694. },
  1695. crosshair: false
  1696. },
  1697. /**
  1698. * Options for the navigator Y axis. Default series options for the
  1699. * navigator yAxis are:
  1700. * ```js
  1701. * yAxis: {
  1702. * gridLineWidth: 0,
  1703. * startOnTick: false,
  1704. * endOnTick: false,
  1705. * minPadding: 0.1,
  1706. * maxPadding: 0.1,
  1707. * labels: {
  1708. * enabled: false
  1709. * },
  1710. * title: {
  1711. * text: null
  1712. * },
  1713. * tickWidth: 0
  1714. * }
  1715. * ```
  1716. *
  1717. * @extends yAxis
  1718. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  1719. * showEmpty, scrollbar, top, units, maxRange, minLength,
  1720. * maxLength, resize
  1721. */
  1722. yAxis: {
  1723. className: 'highcharts-navigator-yaxis',
  1724. gridLineWidth: 0,
  1725. startOnTick: false,
  1726. endOnTick: false,
  1727. minPadding: 0.1,
  1728. maxPadding: 0.1,
  1729. labels: {
  1730. enabled: false
  1731. },
  1732. crosshair: false,
  1733. title: {
  1734. text: null
  1735. },
  1736. tickLength: 0,
  1737. tickWidth: 0
  1738. }
  1739. }
  1740. });
  1741. /* eslint-disable no-invalid-this, valid-jsdoc */
  1742. /**
  1743. * Draw one of the handles on the side of the zoomed range in the navigator
  1744. *
  1745. * @private
  1746. * @function Highcharts.Renderer#symbols.navigator-handle
  1747. * @param {number} x
  1748. * @param {number} y
  1749. * @param {number} w
  1750. * @param {number} h
  1751. * @param {Highcharts.NavigatorHandlesOptions} options
  1752. * @return {Highcharts.SVGPathArray}
  1753. * Path to be used in a handle
  1754. */
  1755. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  1756. var halfWidth = (options && options.width || 0) / 2,
  1757. markerPosition = Math.round(halfWidth / 3) + 0.5,
  1758. height = options && options.height || 0;
  1759. return [
  1760. ['M', -halfWidth - 1, 0.5],
  1761. ['L', halfWidth, 0.5],
  1762. ['L', halfWidth, height + 0.5],
  1763. ['L', -halfWidth - 1, height + 0.5],
  1764. ['L', -halfWidth - 1, 0.5],
  1765. ['M', -markerPosition, 4],
  1766. ['L', -markerPosition, height - 3],
  1767. ['M', markerPosition - 1, 4],
  1768. ['L', markerPosition - 1, height - 3]
  1769. ];
  1770. };
  1771. /**
  1772. * The Navigator class
  1773. *
  1774. * @private
  1775. * @class
  1776. * @name Highcharts.Navigator
  1777. *
  1778. * @param {Highcharts.Chart} chart
  1779. * Chart object
  1780. */
  1781. var Navigator = /** @class */ (function () {
  1782. function Navigator(chart) {
  1783. this.baseSeries = void 0;
  1784. this.chart = void 0;
  1785. this.handles = void 0;
  1786. this.height = void 0;
  1787. this.left = void 0;
  1788. this.navigatorEnabled = void 0;
  1789. this.navigatorGroup = void 0;
  1790. this.navigatorOptions = void 0;
  1791. this.navigatorSeries = void 0;
  1792. this.navigatorSize = void 0;
  1793. this.opposite = void 0;
  1794. this.outline = void 0;
  1795. this.outlineHeight = void 0;
  1796. this.range = void 0;
  1797. this.rendered = void 0;
  1798. this.shades = void 0;
  1799. this.size = void 0;
  1800. this.top = void 0;
  1801. this.xAxis = void 0;
  1802. this.yAxis = void 0;
  1803. this.zoomedMax = void 0;
  1804. this.zoomedMin = void 0;
  1805. this.init(chart);
  1806. }
  1807. /**
  1808. * Draw one of the handles on the side of the zoomed range in the navigator
  1809. *
  1810. * @private
  1811. * @function Highcharts.Navigator#drawHandle
  1812. *
  1813. * @param {number} x
  1814. * The x center for the handle
  1815. *
  1816. * @param {number} index
  1817. * 0 for left and 1 for right
  1818. *
  1819. * @param {boolean|undefined} inverted
  1820. * flag for chart.inverted
  1821. *
  1822. * @param {string} verb
  1823. * use 'animate' or 'attr'
  1824. */
  1825. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  1826. var navigator = this,
  1827. height = navigator.navigatorOptions.handles.height;
  1828. // Place it
  1829. navigator.handles[index][verb](inverted ? {
  1830. translateX: Math.round(navigator.left + navigator.height / 2),
  1831. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  1832. } : {
  1833. translateX: Math.round(navigator.left + parseInt(x, 10)),
  1834. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  1835. });
  1836. };
  1837. /**
  1838. * Render outline around the zoomed range
  1839. *
  1840. * @private
  1841. * @function Highcharts.Navigator#drawOutline
  1842. *
  1843. * @param {number} zoomedMin
  1844. * in pixels position where zoomed range starts
  1845. *
  1846. * @param {number} zoomedMax
  1847. * in pixels position where zoomed range ends
  1848. *
  1849. * @param {boolean|undefined} inverted
  1850. * flag if chart is inverted
  1851. *
  1852. * @param {string} verb
  1853. * use 'animate' or 'attr'
  1854. */
  1855. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  1856. var navigator = this,
  1857. maskInside = navigator.navigatorOptions.maskInside,
  1858. outlineWidth = navigator.outline.strokeWidth(),
  1859. halfOutline = outlineWidth / 2,
  1860. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  1861. outlineHeight = navigator.outlineHeight,
  1862. scrollbarHeight = navigator.scrollbarHeight || 0,
  1863. navigatorSize = navigator.size,
  1864. left = navigator.left - scrollbarHeight,
  1865. navigatorTop = navigator.top,
  1866. verticalMin,
  1867. path;
  1868. if (inverted) {
  1869. left -= halfOutline;
  1870. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  1871. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  1872. path = [
  1873. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  1874. ['L', left + outlineHeight, verticalMin],
  1875. ['L', left, verticalMin],
  1876. ['L', left, zoomedMax],
  1877. ['L', left + outlineHeight, zoomedMax],
  1878. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  1879. ];
  1880. if (maskInside) {
  1881. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  1882. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  1883. );
  1884. }
  1885. }
  1886. else {
  1887. zoomedMin += left + scrollbarHeight - outlineCorrection;
  1888. zoomedMax += left + scrollbarHeight - outlineCorrection;
  1889. navigatorTop += halfOutline;
  1890. path = [
  1891. ['M', left, navigatorTop],
  1892. ['L', zoomedMin, navigatorTop],
  1893. ['L', zoomedMin, navigatorTop + outlineHeight],
  1894. ['L', zoomedMax, navigatorTop + outlineHeight],
  1895. ['L', zoomedMax, navigatorTop],
  1896. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  1897. ];
  1898. if (maskInside) {
  1899. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  1900. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  1901. );
  1902. }
  1903. }
  1904. navigator.outline[verb]({
  1905. d: path
  1906. });
  1907. };
  1908. /**
  1909. * Render outline around the zoomed range
  1910. *
  1911. * @private
  1912. * @function Highcharts.Navigator#drawMasks
  1913. *
  1914. * @param {number} zoomedMin
  1915. * in pixels position where zoomed range starts
  1916. *
  1917. * @param {number} zoomedMax
  1918. * in pixels position where zoomed range ends
  1919. *
  1920. * @param {boolean|undefined} inverted
  1921. * flag if chart is inverted
  1922. *
  1923. * @param {string} verb
  1924. * use 'animate' or 'attr'
  1925. */
  1926. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  1927. var navigator = this,
  1928. left = navigator.left,
  1929. top = navigator.top,
  1930. navigatorHeight = navigator.height,
  1931. height,
  1932. width,
  1933. x,
  1934. y;
  1935. // Determine rectangle position & size
  1936. // According to (non)inverted position:
  1937. if (inverted) {
  1938. x = [left, left, left];
  1939. y = [top, top + zoomedMin, top + zoomedMax];
  1940. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  1941. height = [
  1942. zoomedMin,
  1943. zoomedMax - zoomedMin,
  1944. navigator.size - zoomedMax
  1945. ];
  1946. }
  1947. else {
  1948. x = [left, left + zoomedMin, left + zoomedMax];
  1949. y = [top, top, top];
  1950. width = [
  1951. zoomedMin,
  1952. zoomedMax - zoomedMin,
  1953. navigator.size - zoomedMax
  1954. ];
  1955. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  1956. }
  1957. navigator.shades.forEach(function (shade, i) {
  1958. shade[verb]({
  1959. x: x[i],
  1960. y: y[i],
  1961. width: width[i],
  1962. height: height[i]
  1963. });
  1964. });
  1965. };
  1966. /**
  1967. * Generate DOM elements for a navigator:
  1968. *
  1969. * - main navigator group
  1970. *
  1971. * - all shades
  1972. *
  1973. * - outline
  1974. *
  1975. * - handles
  1976. *
  1977. * @private
  1978. * @function Highcharts.Navigator#renderElements
  1979. */
  1980. Navigator.prototype.renderElements = function () {
  1981. var navigator = this,
  1982. navigatorOptions = navigator.navigatorOptions,
  1983. maskInside = navigatorOptions.maskInside,
  1984. chart = navigator.chart,
  1985. inverted = chart.inverted,
  1986. renderer = chart.renderer,
  1987. navigatorGroup,
  1988. mouseCursor = {
  1989. cursor: inverted ? 'ns-resize' : 'ew-resize'
  1990. };
  1991. // Create the main navigator group
  1992. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  1993. .attr({
  1994. zIndex: 8,
  1995. visibility: 'hidden'
  1996. })
  1997. .add();
  1998. // Create masks, each mask will get events and fill:
  1999. [
  2000. !maskInside,
  2001. maskInside,
  2002. !maskInside
  2003. ].forEach(function (hasMask, index) {
  2004. navigator.shades[index] = renderer.rect()
  2005. .addClass('highcharts-navigator-mask' +
  2006. (index === 1 ? '-inside' : '-outside'))
  2007. .add(navigatorGroup);
  2008. if (!chart.styledMode) {
  2009. navigator.shades[index]
  2010. .attr({
  2011. fill: hasMask ?
  2012. navigatorOptions.maskFill :
  2013. 'rgba(0,0,0,0)'
  2014. })
  2015. .css((index === 1) && mouseCursor);
  2016. }
  2017. });
  2018. // Create the outline:
  2019. navigator.outline = renderer.path()
  2020. .addClass('highcharts-navigator-outline')
  2021. .add(navigatorGroup);
  2022. if (!chart.styledMode) {
  2023. navigator.outline.attr({
  2024. 'stroke-width': navigatorOptions.outlineWidth,
  2025. stroke: navigatorOptions.outlineColor
  2026. });
  2027. }
  2028. // Create the handlers:
  2029. if (navigatorOptions.handles.enabled) {
  2030. [0, 1].forEach(function (index) {
  2031. navigatorOptions.handles.inverted = chart.inverted;
  2032. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  2033. // zIndex = 6 for right handle, 7 for left.
  2034. // Can't be 10, because of the tooltip in inverted chart #2908
  2035. navigator.handles[index].attr({ zIndex: 7 - index })
  2036. .addClass('highcharts-navigator-handle ' +
  2037. 'highcharts-navigator-handle-' +
  2038. ['left', 'right'][index]).add(navigatorGroup);
  2039. if (!chart.styledMode) {
  2040. var handlesOptions = navigatorOptions.handles;
  2041. navigator.handles[index]
  2042. .attr({
  2043. fill: handlesOptions.backgroundColor,
  2044. stroke: handlesOptions.borderColor,
  2045. 'stroke-width': handlesOptions.lineWidth
  2046. })
  2047. .css(mouseCursor);
  2048. }
  2049. });
  2050. }
  2051. };
  2052. /**
  2053. * Update navigator
  2054. *
  2055. * @private
  2056. * @function Highcharts.Navigator#update
  2057. *
  2058. * @param {Highcharts.NavigatorOptions} options
  2059. * Options to merge in when updating navigator
  2060. */
  2061. Navigator.prototype.update = function (options) {
  2062. // Remove references to old navigator series in base series
  2063. (this.series || []).forEach(function (series) {
  2064. if (series.baseSeries) {
  2065. delete series.baseSeries.navigatorSeries;
  2066. }
  2067. });
  2068. // Destroy and rebuild navigator
  2069. this.destroy();
  2070. var chartOptions = this.chart.options;
  2071. merge(true, chartOptions.navigator, this.options, options);
  2072. this.init(this.chart);
  2073. };
  2074. /**
  2075. * Render the navigator
  2076. *
  2077. * @private
  2078. * @function Highcharts.Navigator#render
  2079. * @param {number} min
  2080. * X axis value minimum
  2081. * @param {number} max
  2082. * X axis value maximum
  2083. * @param {number} [pxMin]
  2084. * Pixel value minimum
  2085. * @param {number} [pxMax]
  2086. * Pixel value maximum
  2087. * @return {void}
  2088. */
  2089. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  2090. var navigator = this,
  2091. chart = navigator.chart,
  2092. navigatorWidth,
  2093. scrollbarLeft,
  2094. scrollbarTop,
  2095. scrollbarHeight = navigator.scrollbarHeight,
  2096. navigatorSize,
  2097. xAxis = navigator.xAxis,
  2098. pointRange = xAxis.pointRange || 0,
  2099. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  2100. navigatorEnabled = navigator.navigatorEnabled,
  2101. zoomedMin,
  2102. zoomedMax,
  2103. rendered = navigator.rendered,
  2104. inverted = chart.inverted,
  2105. verb,
  2106. newMin,
  2107. newMax,
  2108. currentRange,
  2109. minRange = chart.xAxis[0].minRange,
  2110. maxRange = chart.xAxis[0].options.maxRange;
  2111. // Don't redraw while moving the handles (#4703).
  2112. if (this.hasDragged && !defined(pxMin)) {
  2113. return;
  2114. }
  2115. min = correctFloat(min - pointRange / 2);
  2116. max = correctFloat(max + pointRange / 2);
  2117. // Don't render the navigator until we have data (#486, #4202, #5172).
  2118. if (!isNumber(min) || !isNumber(max)) {
  2119. // However, if navigator was already rendered, we may need to resize
  2120. // it. For example hidden series, but visible navigator (#6022).
  2121. if (rendered) {
  2122. pxMin = 0;
  2123. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  2124. }
  2125. else {
  2126. return;
  2127. }
  2128. }
  2129. navigator.left = pick(xAxis.left,
  2130. // in case of scrollbar only, without navigator
  2131. chart.plotLeft + scrollbarHeight +
  2132. (inverted ? chart.plotWidth : 0));
  2133. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  2134. 2 * scrollbarHeight);
  2135. if (inverted) {
  2136. navigatorWidth = scrollbarHeight;
  2137. }
  2138. else {
  2139. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  2140. }
  2141. // Get the pixel position of the handles
  2142. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  2143. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  2144. // Verify (#1851, #2238)
  2145. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  2146. pxMin = 0;
  2147. pxMax = navigatorWidth;
  2148. }
  2149. // Are we below the minRange? (#2618, #6191)
  2150. newMin = xAxis.toValue(pxMin, true);
  2151. newMax = xAxis.toValue(pxMax, true);
  2152. currentRange = Math.abs(correctFloat(newMax - newMin));
  2153. if (currentRange < minRange) {
  2154. if (this.grabbedLeft) {
  2155. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  2156. }
  2157. else if (this.grabbedRight) {
  2158. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  2159. }
  2160. }
  2161. else if (defined(maxRange) &&
  2162. correctFloat(currentRange - pointRange) > maxRange) {
  2163. if (this.grabbedLeft) {
  2164. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  2165. }
  2166. else if (this.grabbedRight) {
  2167. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  2168. }
  2169. }
  2170. // Handles are allowed to cross, but never exceed the plot area
  2171. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  2172. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  2173. navigator.zoomedMax - navigator.fixedWidth :
  2174. Math.min(pxMin, pxMax), 0, zoomedMax);
  2175. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  2176. zoomedMax = Math.round(navigator.zoomedMax);
  2177. zoomedMin = Math.round(navigator.zoomedMin);
  2178. if (navigatorEnabled) {
  2179. navigator.navigatorGroup.attr({
  2180. visibility: 'visible'
  2181. });
  2182. // Place elements
  2183. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  2184. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2185. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2186. if (navigator.navigatorOptions.handles.enabled) {
  2187. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2188. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2189. }
  2190. }
  2191. if (navigator.scrollbar) {
  2192. if (inverted) {
  2193. scrollbarTop = navigator.top - scrollbarHeight;
  2194. scrollbarLeft = navigator.left - scrollbarHeight +
  2195. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  2196. // Multiple axes has offsets:
  2197. (scrollbarXAxis.titleOffset || 0) +
  2198. // Self margin from the axis.title
  2199. scrollbarXAxis.axisTitleMargin);
  2200. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  2201. }
  2202. else {
  2203. scrollbarTop = navigator.top + (navigatorEnabled ?
  2204. navigator.height :
  2205. -scrollbarHeight);
  2206. scrollbarLeft = navigator.left - scrollbarHeight;
  2207. }
  2208. // Reposition scrollbar
  2209. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  2210. // Keep scale 0-1
  2211. navigator.scrollbar.setRange(
  2212. // Use real value, not rounded because range can be very small
  2213. // (#1716)
  2214. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  2215. }
  2216. navigator.rendered = true;
  2217. };
  2218. /**
  2219. * Set up the mouse and touch events for the navigator
  2220. *
  2221. * @private
  2222. * @function Highcharts.Navigator#addMouseEvents
  2223. */
  2224. Navigator.prototype.addMouseEvents = function () {
  2225. var navigator = this,
  2226. chart = navigator.chart,
  2227. container = chart.container,
  2228. eventsToUnbind = [],
  2229. mouseMoveHandler,
  2230. mouseUpHandler;
  2231. /**
  2232. * Create mouse events' handlers.
  2233. * Make them as separate functions to enable wrapping them:
  2234. */
  2235. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  2236. navigator.onMouseMove(e);
  2237. };
  2238. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  2239. navigator.onMouseUp(e);
  2240. };
  2241. // Add shades and handles mousedown events
  2242. eventsToUnbind = navigator.getPartsEvents('mousedown');
  2243. // Add mouse move and mouseup events. These are bind to doc/container,
  2244. // because Navigator.grabbedSomething flags are stored in mousedown
  2245. // events
  2246. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  2247. // Touch events
  2248. if (hasTouch) {
  2249. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  2250. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  2251. }
  2252. navigator.eventsToUnbind = eventsToUnbind;
  2253. // Data events
  2254. if (navigator.series && navigator.series[0]) {
  2255. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  2256. chart.navigator.modifyNavigatorAxisExtremes();
  2257. }));
  2258. }
  2259. };
  2260. /**
  2261. * Generate events for handles and masks
  2262. *
  2263. * @private
  2264. * @function Highcharts.Navigator#getPartsEvents
  2265. *
  2266. * @param {string} eventName
  2267. * Event name handler, 'mousedown' or 'touchstart'
  2268. *
  2269. * @return {Array<Function>}
  2270. * An array of functions to remove navigator functions from the
  2271. * events again.
  2272. */
  2273. Navigator.prototype.getPartsEvents = function (eventName) {
  2274. var navigator = this,
  2275. events = [];
  2276. ['shades', 'handles'].forEach(function (name) {
  2277. navigator[name].forEach(function (navigatorItem, index) {
  2278. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  2279. navigator[name + 'Mousedown'](e, index);
  2280. }));
  2281. });
  2282. });
  2283. return events;
  2284. };
  2285. /**
  2286. * Mousedown on a shaded mask, either:
  2287. *
  2288. * - will be stored for future drag&drop
  2289. *
  2290. * - will directly shift to a new range
  2291. *
  2292. * @private
  2293. * @function Highcharts.Navigator#shadesMousedown
  2294. *
  2295. * @param {Highcharts.PointerEventObject} e
  2296. * Mouse event
  2297. *
  2298. * @param {number} index
  2299. * Index of a mask in Navigator.shades array
  2300. */
  2301. Navigator.prototype.shadesMousedown = function (e, index) {
  2302. e = this.chart.pointer.normalize(e);
  2303. var navigator = this,
  2304. chart = navigator.chart,
  2305. xAxis = navigator.xAxis,
  2306. zoomedMin = navigator.zoomedMin,
  2307. navigatorPosition = navigator.left,
  2308. navigatorSize = navigator.size,
  2309. range = navigator.range,
  2310. chartX = e.chartX,
  2311. fixedMax,
  2312. fixedMin,
  2313. ext,
  2314. left;
  2315. // For inverted chart, swap some options:
  2316. if (chart.inverted) {
  2317. chartX = e.chartY;
  2318. navigatorPosition = navigator.top;
  2319. }
  2320. if (index === 1) {
  2321. // Store information for drag&drop
  2322. navigator.grabbedCenter = chartX;
  2323. navigator.fixedWidth = range;
  2324. navigator.dragOffset = chartX - zoomedMin;
  2325. }
  2326. else {
  2327. // Shift the range by clicking on shaded areas
  2328. left = chartX - navigatorPosition - range / 2;
  2329. if (index === 0) {
  2330. left = Math.max(0, left);
  2331. }
  2332. else if (index === 2 && left + range >= navigatorSize) {
  2333. left = navigatorSize - range;
  2334. if (navigator.reversedExtremes) {
  2335. // #7713
  2336. left -= range;
  2337. fixedMin = navigator.getUnionExtremes().dataMin;
  2338. }
  2339. else {
  2340. // #2293, #3543
  2341. fixedMax = navigator.getUnionExtremes().dataMax;
  2342. }
  2343. }
  2344. if (left !== zoomedMin) { // it has actually moved
  2345. navigator.fixedWidth = range; // #1370
  2346. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  2347. if (defined(ext.min)) { // #7411
  2348. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  2349. { trigger: 'navigator' });
  2350. }
  2351. }
  2352. }
  2353. };
  2354. /**
  2355. * Mousedown on a handle mask.
  2356. * Will store necessary information for drag&drop.
  2357. *
  2358. * @private
  2359. * @function Highcharts.Navigator#handlesMousedown
  2360. * @param {Highcharts.PointerEventObject} e
  2361. * Mouse event
  2362. * @param {number} index
  2363. * Index of a handle in Navigator.handles array
  2364. * @return {void}
  2365. */
  2366. Navigator.prototype.handlesMousedown = function (e, index) {
  2367. e = this.chart.pointer.normalize(e);
  2368. var navigator = this,
  2369. chart = navigator.chart,
  2370. baseXAxis = chart.xAxis[0],
  2371. // For reversed axes, min and max are changed,
  2372. // so the other extreme should be stored
  2373. reverse = navigator.reversedExtremes;
  2374. if (index === 0) {
  2375. // Grab the left handle
  2376. navigator.grabbedLeft = true;
  2377. navigator.otherHandlePos = navigator.zoomedMax;
  2378. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  2379. }
  2380. else {
  2381. // Grab the right handle
  2382. navigator.grabbedRight = true;
  2383. navigator.otherHandlePos = navigator.zoomedMin;
  2384. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  2385. }
  2386. chart.fixedRange = null;
  2387. };
  2388. /**
  2389. * Mouse move event based on x/y mouse position.
  2390. *
  2391. * @private
  2392. * @function Highcharts.Navigator#onMouseMove
  2393. *
  2394. * @param {Highcharts.PointerEventObject} e
  2395. * Mouse event
  2396. */
  2397. Navigator.prototype.onMouseMove = function (e) {
  2398. var navigator = this,
  2399. chart = navigator.chart,
  2400. left = navigator.left,
  2401. navigatorSize = navigator.navigatorSize,
  2402. range = navigator.range,
  2403. dragOffset = navigator.dragOffset,
  2404. inverted = chart.inverted,
  2405. chartX;
  2406. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  2407. // the finger down in the center of the scrollbar. This should be
  2408. // ignored.
  2409. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  2410. e = chart.pointer.normalize(e);
  2411. chartX = e.chartX;
  2412. // Swap some options for inverted chart
  2413. if (inverted) {
  2414. left = navigator.top;
  2415. chartX = e.chartY;
  2416. }
  2417. // Drag left handle or top handle
  2418. if (navigator.grabbedLeft) {
  2419. navigator.hasDragged = true;
  2420. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  2421. // Drag right handle or bottom handle
  2422. }
  2423. else if (navigator.grabbedRight) {
  2424. navigator.hasDragged = true;
  2425. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  2426. // Drag scrollbar or open area in navigator
  2427. }
  2428. else if (navigator.grabbedCenter) {
  2429. navigator.hasDragged = true;
  2430. if (chartX < dragOffset) { // outside left
  2431. chartX = dragOffset;
  2432. // outside right
  2433. }
  2434. else if (chartX >
  2435. navigatorSize + dragOffset - range) {
  2436. chartX = navigatorSize + dragOffset - range;
  2437. }
  2438. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  2439. }
  2440. if (navigator.hasDragged &&
  2441. navigator.scrollbar &&
  2442. pick(navigator.scrollbar.options.liveRedraw,
  2443. // By default, don't run live redraw on VML, on touch
  2444. // devices or if the chart is in boost.
  2445. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  2446. e.DOMType = e.type; // DOMType is for IE8
  2447. setTimeout(function () {
  2448. navigator.onMouseUp(e);
  2449. }, 0);
  2450. }
  2451. }
  2452. };
  2453. /**
  2454. * Mouse up event based on x/y mouse position.
  2455. *
  2456. * @private
  2457. * @function Highcharts.Navigator#onMouseUp
  2458. * @param {Highcharts.PointerEventObject} e
  2459. * Mouse event
  2460. * @return {void}
  2461. */
  2462. Navigator.prototype.onMouseUp = function (e) {
  2463. var navigator = this,
  2464. chart = navigator.chart,
  2465. xAxis = navigator.xAxis,
  2466. scrollbar = navigator.scrollbar,
  2467. DOMEvent = e.DOMEvent || e,
  2468. inverted = chart.inverted,
  2469. verb = navigator.rendered && !navigator.hasDragged ?
  2470. 'animate' : 'attr',
  2471. zoomedMax,
  2472. zoomedMin,
  2473. unionExtremes,
  2474. fixedMin,
  2475. fixedMax,
  2476. ext;
  2477. if (
  2478. // MouseUp is called for both, navigator and scrollbar (that order),
  2479. // which causes calling afterSetExtremes twice. Prevent first call
  2480. // by checking if scrollbar is going to set new extremes (#6334)
  2481. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  2482. e.trigger === 'scrollbar') {
  2483. unionExtremes = navigator.getUnionExtremes();
  2484. // When dragging one handle, make sure the other one doesn't change
  2485. if (navigator.zoomedMin === navigator.otherHandlePos) {
  2486. fixedMin = navigator.fixedExtreme;
  2487. }
  2488. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  2489. fixedMax = navigator.fixedExtreme;
  2490. }
  2491. // Snap to right edge (#4076)
  2492. if (navigator.zoomedMax === navigator.size) {
  2493. fixedMax = navigator.reversedExtremes ?
  2494. unionExtremes.dataMin :
  2495. unionExtremes.dataMax;
  2496. }
  2497. // Snap to left edge (#7576)
  2498. if (navigator.zoomedMin === 0) {
  2499. fixedMin = navigator.reversedExtremes ?
  2500. unionExtremes.dataMax :
  2501. unionExtremes.dataMin;
  2502. }
  2503. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  2504. if (defined(ext.min)) {
  2505. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  2506. // Run animation when clicking buttons, scrollbar track etc,
  2507. // but not when dragging handles or scrollbar
  2508. navigator.hasDragged ? false : null, {
  2509. trigger: 'navigator',
  2510. triggerOp: 'navigator-drag',
  2511. DOMEvent: DOMEvent // #1838
  2512. });
  2513. }
  2514. }
  2515. if (e.DOMType !== 'mousemove' &&
  2516. e.DOMType !== 'touchmove') {
  2517. navigator.grabbedLeft = navigator.grabbedRight =
  2518. navigator.grabbedCenter = navigator.fixedWidth =
  2519. navigator.fixedExtreme = navigator.otherHandlePos =
  2520. navigator.hasDragged = navigator.dragOffset = null;
  2521. }
  2522. // Update position of navigator shades, outline and handles (#12573)
  2523. if (navigator.navigatorEnabled &&
  2524. isNumber(navigator.zoomedMin) &&
  2525. isNumber(navigator.zoomedMax)) {
  2526. zoomedMin = Math.round(navigator.zoomedMin);
  2527. zoomedMax = Math.round(navigator.zoomedMax);
  2528. if (navigator.shades) {
  2529. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2530. }
  2531. if (navigator.outline) {
  2532. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2533. }
  2534. if (navigator.navigatorOptions.handles.enabled &&
  2535. Object.keys(navigator.handles).length ===
  2536. navigator.handles.length) {
  2537. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2538. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2539. }
  2540. }
  2541. };
  2542. /**
  2543. * Removes the event handlers attached previously with addEvents.
  2544. *
  2545. * @private
  2546. * @function Highcharts.Navigator#removeEvents
  2547. * @return {void}
  2548. */
  2549. Navigator.prototype.removeEvents = function () {
  2550. if (this.eventsToUnbind) {
  2551. this.eventsToUnbind.forEach(function (unbind) {
  2552. unbind();
  2553. });
  2554. this.eventsToUnbind = void 0;
  2555. }
  2556. this.removeBaseSeriesEvents();
  2557. };
  2558. /**
  2559. * Remove data events.
  2560. *
  2561. * @private
  2562. * @function Highcharts.Navigator#removeBaseSeriesEvents
  2563. * @return {void}
  2564. */
  2565. Navigator.prototype.removeBaseSeriesEvents = function () {
  2566. var baseSeries = this.baseSeries || [];
  2567. if (this.navigatorEnabled && baseSeries[0]) {
  2568. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2569. baseSeries.forEach(function (series) {
  2570. removeEvent(series, 'updatedData', this.updatedDataHandler);
  2571. }, this);
  2572. }
  2573. // We only listen for extremes-events on the first baseSeries
  2574. if (baseSeries[0].xAxis) {
  2575. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2576. }
  2577. }
  2578. };
  2579. /**
  2580. * Initialize the Navigator object
  2581. *
  2582. * @private
  2583. * @function Highcharts.Navigator#init
  2584. *
  2585. * @param {Highcharts.Chart} chart
  2586. */
  2587. Navigator.prototype.init = function (chart) {
  2588. var chartOptions = chart.options,
  2589. navigatorOptions = chartOptions.navigator,
  2590. navigatorEnabled = navigatorOptions.enabled,
  2591. scrollbarOptions = chartOptions.scrollbar,
  2592. scrollbarEnabled = scrollbarOptions.enabled,
  2593. height = navigatorEnabled ? navigatorOptions.height : 0,
  2594. scrollbarHeight = scrollbarEnabled ?
  2595. scrollbarOptions.height :
  2596. 0;
  2597. this.handles = [];
  2598. this.shades = [];
  2599. this.chart = chart;
  2600. this.setBaseSeries();
  2601. this.height = height;
  2602. this.scrollbarHeight = scrollbarHeight;
  2603. this.scrollbarEnabled = scrollbarEnabled;
  2604. this.navigatorEnabled = navigatorEnabled;
  2605. this.navigatorOptions = navigatorOptions;
  2606. this.scrollbarOptions = scrollbarOptions;
  2607. this.outlineHeight = height + scrollbarHeight;
  2608. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  2609. var navigator = this,
  2610. baseSeries = navigator.baseSeries,
  2611. xAxisIndex = chart.xAxis.length,
  2612. yAxisIndex = chart.yAxis.length,
  2613. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  2614. chart.xAxis[0] || { options: {} };
  2615. chart.isDirtyBox = true;
  2616. if (navigator.navigatorEnabled) {
  2617. // an x axis is required for scrollbar also
  2618. navigator.xAxis = new Axis(chart, merge({
  2619. // inherit base xAxis' break and ordinal options
  2620. breaks: baseXaxis.options.breaks,
  2621. ordinal: baseXaxis.options.ordinal
  2622. }, navigatorOptions.xAxis, {
  2623. id: 'navigator-x-axis',
  2624. yAxis: 'navigator-y-axis',
  2625. isX: true,
  2626. type: 'datetime',
  2627. index: xAxisIndex,
  2628. isInternal: true,
  2629. offset: 0,
  2630. keepOrdinalPadding: true,
  2631. startOnTick: false,
  2632. endOnTick: false,
  2633. minPadding: 0,
  2634. maxPadding: 0,
  2635. zoomEnabled: false
  2636. }, chart.inverted ? {
  2637. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  2638. width: height
  2639. } : {
  2640. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  2641. height: height
  2642. }));
  2643. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  2644. id: 'navigator-y-axis',
  2645. alignTicks: false,
  2646. offset: 0,
  2647. index: yAxisIndex,
  2648. isInternal: true,
  2649. reversed: pick((navigatorOptions.yAxis && navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  2650. zoomEnabled: false
  2651. }, chart.inverted ? {
  2652. width: height
  2653. } : {
  2654. height: height
  2655. }));
  2656. // If we have a base series, initialize the navigator series
  2657. if (baseSeries || navigatorOptions.series.data) {
  2658. navigator.updateNavigatorSeries(false);
  2659. // If not, set up an event to listen for added series
  2660. }
  2661. else if (chart.series.length === 0) {
  2662. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  2663. // We've got one, now add it as base
  2664. if (chart.series.length > 0 && !navigator.series) {
  2665. navigator.setBaseSeries();
  2666. navigator.unbindRedraw(); // reset
  2667. }
  2668. });
  2669. }
  2670. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  2671. // Render items, so we can bind events to them:
  2672. navigator.renderElements();
  2673. // Add mouse events
  2674. navigator.addMouseEvents();
  2675. // in case of scrollbar only, fake an x axis to get translation
  2676. }
  2677. else {
  2678. navigator.xAxis = {
  2679. chart: chart,
  2680. navigatorAxis: {
  2681. fake: true
  2682. },
  2683. translate: function (value, reverse) {
  2684. 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;
  2685. return reverse ?
  2686. // from pixel to value
  2687. (value * valueRange / scrollTrackWidth) + min :
  2688. // from value to pixel
  2689. scrollTrackWidth * (value - min) / valueRange;
  2690. },
  2691. toPixels: function (value) {
  2692. return this.translate(value);
  2693. },
  2694. toValue: function (value) {
  2695. return this.translate(value, true);
  2696. }
  2697. };
  2698. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  2699. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  2700. }
  2701. // Initialize the scrollbar
  2702. if (chart.options.scrollbar.enabled) {
  2703. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  2704. margin: navigator.navigatorEnabled ? 0 : 10,
  2705. vertical: chart.inverted
  2706. }), chart);
  2707. addEvent(navigator.scrollbar, 'changed', function (e) {
  2708. var range = navigator.size,
  2709. to = range * this.to,
  2710. from = range * this.from;
  2711. navigator.hasDragged = navigator.scrollbar.hasDragged;
  2712. navigator.render(0, 0, from, to);
  2713. if (chart.options.scrollbar.liveRedraw ||
  2714. (e.DOMType !== 'mousemove' &&
  2715. e.DOMType !== 'touchmove')) {
  2716. setTimeout(function () {
  2717. navigator.onMouseUp(e);
  2718. });
  2719. }
  2720. });
  2721. }
  2722. // Add data events
  2723. navigator.addBaseSeriesEvents();
  2724. // Add redraw events
  2725. navigator.addChartEvents();
  2726. };
  2727. /**
  2728. * Get the union data extremes of the chart - the outer data extremes of the
  2729. * base X axis and the navigator axis.
  2730. *
  2731. * @private
  2732. * @function Highcharts.Navigator#getUnionExtremes
  2733. * @param {boolean} [returnFalseOnNoBaseSeries]
  2734. * as the param says.
  2735. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  2736. */
  2737. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  2738. var baseAxis = this.chart.xAxis[0],
  2739. navAxis = this.xAxis,
  2740. navAxisOptions = navAxis.options,
  2741. baseAxisOptions = baseAxis.options,
  2742. ret;
  2743. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  2744. ret = {
  2745. dataMin: pick(// #4053
  2746. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  2747. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  2748. };
  2749. }
  2750. return ret;
  2751. };
  2752. /**
  2753. * Set the base series and update the navigator series from this. With a bit
  2754. * of modification we should be able to make this an API method to be called
  2755. * from the outside
  2756. *
  2757. * @private
  2758. * @function Highcharts.Navigator#setBaseSeries
  2759. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  2760. * Additional series options for a navigator
  2761. * @param {boolean} [redraw]
  2762. * Whether to redraw after update.
  2763. * @return {void}
  2764. */
  2765. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  2766. var chart = this.chart,
  2767. baseSeries = this.baseSeries = [];
  2768. baseSeriesOptions = (baseSeriesOptions ||
  2769. chart.options && chart.options.navigator.baseSeries ||
  2770. (chart.series.length ?
  2771. // Find the first non-navigator series (#8430)
  2772. find(chart.series, function (s) {
  2773. return !s.options.isInternal;
  2774. }).index :
  2775. 0));
  2776. // Iterate through series and add the ones that should be shown in
  2777. // navigator.
  2778. (chart.series || []).forEach(function (series, i) {
  2779. if (
  2780. // Don't include existing nav series
  2781. !series.options.isInternal &&
  2782. (series.options.showInNavigator ||
  2783. (i === baseSeriesOptions ||
  2784. series.options.id === baseSeriesOptions) &&
  2785. series.options.showInNavigator !== false)) {
  2786. baseSeries.push(series);
  2787. }
  2788. });
  2789. // When run after render, this.xAxis already exists
  2790. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  2791. this.updateNavigatorSeries(true, redraw);
  2792. }
  2793. };
  2794. /**
  2795. * Update series in the navigator from baseSeries, adding new if does not
  2796. * exist.
  2797. *
  2798. * @private
  2799. * @function Highcharts.Navigator.updateNavigatorSeries
  2800. * @param {boolean} addEvents
  2801. * @param {boolean} [redraw]
  2802. * @return {void}
  2803. */
  2804. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  2805. var navigator = this,
  2806. chart = navigator.chart,
  2807. baseSeries = navigator.baseSeries,
  2808. baseOptions,
  2809. mergedNavSeriesOptions,
  2810. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  2811. baseNavigatorOptions,
  2812. navSeriesMixin = {
  2813. enableMouseTracking: false,
  2814. index: null,
  2815. linkedTo: null,
  2816. group: 'nav',
  2817. padXAxis: false,
  2818. xAxis: 'navigator-x-axis',
  2819. yAxis: 'navigator-y-axis',
  2820. showInLegend: false,
  2821. stacking: void 0,
  2822. isInternal: true,
  2823. states: {
  2824. inactive: {
  2825. opacity: 1
  2826. }
  2827. }
  2828. },
  2829. // Remove navigator series that are no longer in the baseSeries
  2830. navigatorSeries = navigator.series =
  2831. (navigator.series || []).filter(function (navSeries) {
  2832. var base = navSeries.baseSeries;
  2833. if (baseSeries.indexOf(base) < 0) { // Not in array
  2834. // If there is still a base series connected to this
  2835. // series, remove event handler and reference.
  2836. if (base) {
  2837. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  2838. delete base.navigatorSeries;
  2839. }
  2840. // Kill the nav series. It may already have been
  2841. // destroyed (#8715).
  2842. if (navSeries.chart) {
  2843. navSeries.destroy();
  2844. }
  2845. return false;
  2846. }
  2847. return true;
  2848. });
  2849. // Go through each base series and merge the options to create new
  2850. // series
  2851. if (baseSeries && baseSeries.length) {
  2852. baseSeries.forEach(function eachBaseSeries(base) {
  2853. var linkedNavSeries = base.navigatorSeries,
  2854. userNavOptions = extend(
  2855. // Grab color and visibility from base as default
  2856. {
  2857. color: base.color,
  2858. visible: base.visible
  2859. }, !isArray(chartNavigatorSeriesOptions) ?
  2860. chartNavigatorSeriesOptions :
  2861. defaultOptions.navigator.series);
  2862. // Don't update if the series exists in nav and we have disabled
  2863. // adaptToUpdatedData.
  2864. if (linkedNavSeries &&
  2865. navigator.navigatorOptions.adaptToUpdatedData === false) {
  2866. return;
  2867. }
  2868. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  2869. baseOptions = base.options || {};
  2870. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  2871. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  2872. // Once nav series type is resolved, pick correct pointRange
  2873. mergedNavSeriesOptions.pointRange = pick(
  2874. // Stricte set pointRange in options
  2875. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  2876. // Fallback to default values, e.g. `null` for column
  2877. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  2878. // Merge data separately. Do a slice to avoid mutating the
  2879. // navigator options from base series (#4923).
  2880. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  2881. navigator.hasNavigatorData =
  2882. navigator.hasNavigatorData || !!navigatorSeriesData;
  2883. mergedNavSeriesOptions.data =
  2884. navigatorSeriesData ||
  2885. baseOptions.data && baseOptions.data.slice(0);
  2886. // Update or add the series
  2887. if (linkedNavSeries && linkedNavSeries.options) {
  2888. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  2889. }
  2890. else {
  2891. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  2892. base.navigatorSeries.baseSeries = base; // Store ref
  2893. navigatorSeries.push(base.navigatorSeries);
  2894. }
  2895. });
  2896. }
  2897. // If user has defined data (and no base series) or explicitly defined
  2898. // navigator.series as an array, we create these series on top of any
  2899. // base series.
  2900. if (chartNavigatorSeriesOptions.data &&
  2901. !(baseSeries && baseSeries.length) ||
  2902. isArray(chartNavigatorSeriesOptions)) {
  2903. navigator.hasNavigatorData = false;
  2904. // Allow navigator.series to be an array
  2905. chartNavigatorSeriesOptions =
  2906. splat(chartNavigatorSeriesOptions);
  2907. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  2908. navSeriesMixin.name =
  2909. 'Navigator ' + (navigatorSeries.length + 1);
  2910. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  2911. // Since we don't have a base series to pull color from,
  2912. // try to fake it by using color from series with same
  2913. // index. Otherwise pull from the colors array. We need
  2914. // an explicit color as otherwise updates will increment
  2915. // color counter and we'll get a new color for each
  2916. // update of the nav series.
  2917. color: chart.series[i] &&
  2918. !chart.series[i].options.isInternal &&
  2919. chart.series[i].color ||
  2920. chart.options.colors[i] ||
  2921. chart.options.colors[0]
  2922. }, navSeriesMixin, userSeriesOptions);
  2923. mergedNavSeriesOptions.data = userSeriesOptions.data;
  2924. if (mergedNavSeriesOptions.data) {
  2925. navigator.hasNavigatorData = true;
  2926. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  2927. }
  2928. });
  2929. }
  2930. if (addEvents) {
  2931. this.addBaseSeriesEvents();
  2932. }
  2933. };
  2934. /**
  2935. * Add data events.
  2936. * For example when main series is updated we need to recalculate extremes
  2937. *
  2938. * @private
  2939. * @function Highcharts.Navigator#addBaseSeriesEvent
  2940. * @return {void}
  2941. */
  2942. Navigator.prototype.addBaseSeriesEvents = function () {
  2943. var navigator = this,
  2944. baseSeries = navigator.baseSeries || [];
  2945. // Bind modified extremes event to first base's xAxis only.
  2946. // In event of > 1 base-xAxes, the navigator will ignore those.
  2947. // Adding this multiple times to the same axis is no problem, as
  2948. // duplicates should be discarded by the browser.
  2949. if (baseSeries[0] && baseSeries[0].xAxis) {
  2950. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2951. }
  2952. baseSeries.forEach(function (base) {
  2953. // Link base series show/hide to navigator series visibility
  2954. addEvent(base, 'show', function () {
  2955. if (this.navigatorSeries) {
  2956. this.navigatorSeries.setVisible(true, false);
  2957. }
  2958. });
  2959. addEvent(base, 'hide', function () {
  2960. if (this.navigatorSeries) {
  2961. this.navigatorSeries.setVisible(false, false);
  2962. }
  2963. });
  2964. // Respond to updated data in the base series, unless explicitily
  2965. // not adapting to data changes.
  2966. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2967. if (base.xAxis) {
  2968. addEvent(base, 'updatedData', this.updatedDataHandler);
  2969. }
  2970. }
  2971. // Handle series removal
  2972. addEvent(base, 'remove', function () {
  2973. if (this.navigatorSeries) {
  2974. erase(navigator.series, this.navigatorSeries);
  2975. if (defined(this.navigatorSeries.options)) {
  2976. this.navigatorSeries.remove(false);
  2977. }
  2978. delete this.navigatorSeries;
  2979. }
  2980. });
  2981. }, this);
  2982. };
  2983. /**
  2984. * Get minimum from all base series connected to the navigator
  2985. * @private
  2986. * @param {number} currentSeriesMin
  2987. * Minium from the current series
  2988. * @return {number} Minimum from all series
  2989. */
  2990. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  2991. return this.baseSeries.reduce(function (min, series) {
  2992. // (#10193)
  2993. return Math.min(min, series.xData ? series.xData[0] : min);
  2994. }, currentSeriesMin);
  2995. };
  2996. /**
  2997. * Set the navigator x axis extremes to reflect the total. The navigator
  2998. * extremes should always be the extremes of the union of all series in the
  2999. * chart as well as the navigator series.
  3000. *
  3001. * @private
  3002. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  3003. */
  3004. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  3005. var xAxis = this.xAxis,
  3006. unionExtremes;
  3007. if (typeof xAxis.getExtremes !== 'undefined') {
  3008. unionExtremes = this.getUnionExtremes(true);
  3009. if (unionExtremes &&
  3010. (unionExtremes.dataMin !== xAxis.min ||
  3011. unionExtremes.dataMax !== xAxis.max)) {
  3012. xAxis.min = unionExtremes.dataMin;
  3013. xAxis.max = unionExtremes.dataMax;
  3014. }
  3015. }
  3016. };
  3017. /**
  3018. * Hook to modify the base axis extremes with information from the Navigator
  3019. *
  3020. * @private
  3021. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  3022. */
  3023. Navigator.prototype.modifyBaseAxisExtremes = function () {
  3024. var baseXAxis = this,
  3025. navigator = baseXAxis.chart.navigator,
  3026. baseExtremes = baseXAxis.getExtremes(),
  3027. baseMin = baseExtremes.min,
  3028. baseMax = baseExtremes.max,
  3029. baseDataMin = baseExtremes.dataMin,
  3030. baseDataMax = baseExtremes.dataMax,
  3031. range = baseMax - baseMin,
  3032. stickToMin = navigator.stickToMin,
  3033. stickToMax = navigator.stickToMax,
  3034. overscroll = pick(baseXAxis.options.overscroll, 0),
  3035. newMax,
  3036. newMin,
  3037. navigatorSeries = navigator.series && navigator.series[0],
  3038. hasSetExtremes = !!baseXAxis.setExtremes,
  3039. // When the extremes have been set by range selector button, don't
  3040. // stick to min or max. The range selector buttons will handle the
  3041. // extremes. (#5489)
  3042. unmutable = baseXAxis.eventArgs &&
  3043. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  3044. if (!unmutable) {
  3045. // If the zoomed range is already at the min, move it to the right
  3046. // as new data comes in
  3047. if (stickToMin) {
  3048. newMin = baseDataMin;
  3049. newMax = newMin + range;
  3050. }
  3051. // If the zoomed range is already at the max, move it to the right
  3052. // as new data comes in
  3053. if (stickToMax) {
  3054. newMax = baseDataMax + overscroll;
  3055. // If stickToMin is true, the new min value is set above
  3056. if (!stickToMin) {
  3057. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  3058. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  3059. navigatorSeries.xData[0] :
  3060. -Number.MAX_VALUE));
  3061. }
  3062. }
  3063. // Update the extremes
  3064. if (hasSetExtremes && (stickToMin || stickToMax)) {
  3065. if (isNumber(newMin)) {
  3066. baseXAxis.min = baseXAxis.userMin = newMin;
  3067. baseXAxis.max = baseXAxis.userMax = newMax;
  3068. }
  3069. }
  3070. }
  3071. // Reset
  3072. navigator.stickToMin =
  3073. navigator.stickToMax = null;
  3074. };
  3075. /**
  3076. * Handler for updated data on the base series. When data is modified, the
  3077. * navigator series must reflect it. This is called from the Chart.redraw
  3078. * function before axis and series extremes are computed.
  3079. *
  3080. * @private
  3081. * @function Highcharts.Navigator#updateDataHandler
  3082. */
  3083. Navigator.prototype.updatedDataHandler = function () {
  3084. var navigator = this.chart.navigator,
  3085. baseSeries = this,
  3086. navigatorSeries = this.navigatorSeries,
  3087. xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  3088. // If the scrollbar is scrolled all the way to the right, keep right as
  3089. // new data comes in.
  3090. navigator.stickToMax = navigator.reversedExtremes ?
  3091. Math.round(navigator.zoomedMin) === 0 :
  3092. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  3093. // Detect whether the zoomed area should stick to the minimum or
  3094. // maximum. If the current axis minimum falls outside the new updated
  3095. // dataset, we must adjust.
  3096. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  3097. (baseSeries.xAxis.min <= xDataMin) &&
  3098. (!this.chart.fixedRange || !navigator.stickToMax);
  3099. // Set the navigator series data to the new data of the base series
  3100. if (navigatorSeries && !navigator.hasNavigatorData) {
  3101. navigatorSeries.options.pointStart = baseSeries.xData[0];
  3102. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  3103. }
  3104. };
  3105. /**
  3106. * Add chart events, like redrawing navigator, when chart requires that.
  3107. *
  3108. * @private
  3109. * @function Highcharts.Navigator#addChartEvents
  3110. * @return {void}
  3111. */
  3112. Navigator.prototype.addChartEvents = function () {
  3113. if (!this.eventsToUnbind) {
  3114. this.eventsToUnbind = [];
  3115. }
  3116. this.eventsToUnbind.push(
  3117. // Move the scrollbar after redraw, like after data updata even if
  3118. // axes don't redraw
  3119. addEvent(this.chart, 'redraw', function () {
  3120. var navigator = this.navigator,
  3121. xAxis = navigator && (navigator.baseSeries &&
  3122. navigator.baseSeries[0] &&
  3123. navigator.baseSeries[0].xAxis ||
  3124. this.xAxis[0]); // #5709, #13114
  3125. if (xAxis) {
  3126. navigator.render(xAxis.min,
  3127. xAxis.max);
  3128. }
  3129. }),
  3130. // Make room for the navigator, can be placed around the chart:
  3131. addEvent(this.chart, 'getMargins', function () {
  3132. var chart = this,
  3133. navigator = chart.navigator,
  3134. marginName = navigator.opposite ?
  3135. 'plotTop' : 'marginBottom';
  3136. if (chart.inverted) {
  3137. marginName = navigator.opposite ?
  3138. 'marginRight' : 'plotLeft';
  3139. }
  3140. chart[marginName] =
  3141. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  3142. navigator.outlineHeight :
  3143. 0) + navigator.navigatorOptions.margin;
  3144. }));
  3145. };
  3146. /**
  3147. * Destroys allocated elements.
  3148. *
  3149. * @private
  3150. * @function Highcharts.Navigator#destroy
  3151. */
  3152. Navigator.prototype.destroy = function () {
  3153. // Disconnect events added in addEvents
  3154. this.removeEvents();
  3155. if (this.xAxis) {
  3156. erase(this.chart.xAxis, this.xAxis);
  3157. erase(this.chart.axes, this.xAxis);
  3158. }
  3159. if (this.yAxis) {
  3160. erase(this.chart.yAxis, this.yAxis);
  3161. erase(this.chart.axes, this.yAxis);
  3162. }
  3163. // Destroy series
  3164. (this.series || []).forEach(function (s) {
  3165. if (s.destroy) {
  3166. s.destroy();
  3167. }
  3168. });
  3169. // Destroy properties
  3170. [
  3171. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  3172. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  3173. 'rendered'
  3174. ].forEach(function (prop) {
  3175. if (this[prop] && this[prop].destroy) {
  3176. this[prop].destroy();
  3177. }
  3178. this[prop] = null;
  3179. }, this);
  3180. // Destroy elements in collection
  3181. [this.handles].forEach(function (coll) {
  3182. destroyObjectProperties(coll);
  3183. }, this);
  3184. };
  3185. return Navigator;
  3186. }());
  3187. // End of prototype
  3188. if (!H.Navigator) {
  3189. H.Navigator = Navigator;
  3190. NavigatorAxis.compose(Axis);
  3191. // For Stock charts. For x only zooming, do not to create the zoom button
  3192. // because X axis zooming is already allowed by the Navigator and Range
  3193. // selector. (#9285)
  3194. addEvent(Chart, 'beforeShowResetZoom', function () {
  3195. var chartOptions = this.options,
  3196. navigator = chartOptions.navigator,
  3197. rangeSelector = chartOptions.rangeSelector;
  3198. if (((navigator && navigator.enabled) ||
  3199. (rangeSelector && rangeSelector.enabled)) &&
  3200. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  3201. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  3202. return false;
  3203. }
  3204. });
  3205. // Initialize navigator for stock charts
  3206. addEvent(Chart, 'beforeRender', function () {
  3207. var options = this.options;
  3208. if (options.navigator.enabled ||
  3209. options.scrollbar.enabled) {
  3210. this.scroller = this.navigator = new Navigator(this);
  3211. }
  3212. });
  3213. // For stock charts, extend the Chart.setChartSize method so that we can set
  3214. // the final top position of the navigator once the height of the chart,
  3215. // including the legend, is determined. #367. We can't use Chart.getMargins,
  3216. // because labels offsets are not calculated yet.
  3217. addEvent(Chart, 'afterSetChartSize', function () {
  3218. var legend = this.legend,
  3219. navigator = this.navigator,
  3220. scrollbarHeight,
  3221. legendOptions,
  3222. xAxis,
  3223. yAxis;
  3224. if (navigator) {
  3225. legendOptions = legend && legend.options;
  3226. xAxis = navigator.xAxis;
  3227. yAxis = navigator.yAxis;
  3228. scrollbarHeight = navigator.scrollbarHeight;
  3229. // Compute the top position
  3230. if (this.inverted) {
  3231. navigator.left = navigator.opposite ?
  3232. this.chartWidth - scrollbarHeight -
  3233. navigator.height :
  3234. this.spacing[3] + scrollbarHeight;
  3235. navigator.top = this.plotTop + scrollbarHeight;
  3236. }
  3237. else {
  3238. navigator.left = this.plotLeft + scrollbarHeight;
  3239. navigator.top = navigator.navigatorOptions.top ||
  3240. this.chartHeight -
  3241. navigator.height -
  3242. scrollbarHeight -
  3243. this.spacing[2] -
  3244. (this.rangeSelector && this.extraBottomMargin ?
  3245. this.rangeSelector.getHeight() :
  3246. 0) -
  3247. ((legendOptions &&
  3248. legendOptions.verticalAlign === 'bottom' &&
  3249. legendOptions.layout !== 'proximate' && // #13392
  3250. legendOptions.enabled &&
  3251. !legendOptions.floating) ?
  3252. legend.legendHeight +
  3253. pick(legendOptions.margin, 10) :
  3254. 0) -
  3255. (this.titleOffset ? this.titleOffset[2] : 0);
  3256. }
  3257. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  3258. if (this.inverted) {
  3259. xAxis.options.left = yAxis.options.left = navigator.left;
  3260. }
  3261. else {
  3262. xAxis.options.top = yAxis.options.top = navigator.top;
  3263. }
  3264. xAxis.setAxisSize();
  3265. yAxis.setAxisSize();
  3266. }
  3267. }
  3268. });
  3269. // Merge options, if no scrolling exists yet
  3270. addEvent(Chart, 'update', function (e) {
  3271. var navigatorOptions = (e.options.navigator || {}),
  3272. scrollbarOptions = (e.options.scrollbar || {});
  3273. if (!this.navigator && !this.scroller &&
  3274. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  3275. merge(true, this.options.navigator, navigatorOptions);
  3276. merge(true, this.options.scrollbar, scrollbarOptions);
  3277. delete e.options.navigator;
  3278. delete e.options.scrollbar;
  3279. }
  3280. });
  3281. // Initialize navigator, if no scrolling exists yet
  3282. addEvent(Chart, 'afterUpdate', function (event) {
  3283. if (!this.navigator && !this.scroller &&
  3284. (this.options.navigator.enabled ||
  3285. this.options.scrollbar.enabled)) {
  3286. this.scroller = this.navigator = new Navigator(this);
  3287. if (pick(event.redraw, true)) {
  3288. this.redraw(event.animation); // #7067
  3289. }
  3290. }
  3291. });
  3292. // Handle adding new series
  3293. addEvent(Chart, 'afterAddSeries', function () {
  3294. if (this.navigator) {
  3295. // Recompute which series should be shown in navigator, and add them
  3296. this.navigator.setBaseSeries(null, false);
  3297. }
  3298. });
  3299. // Handle updating series
  3300. addEvent(Series, 'afterUpdate', function () {
  3301. if (this.chart.navigator && !this.options.isInternal) {
  3302. this.chart.navigator.setBaseSeries(null, false);
  3303. }
  3304. });
  3305. Chart.prototype.callbacks.push(function (chart) {
  3306. var extremes,
  3307. navigator = chart.navigator;
  3308. // Initialize the navigator
  3309. if (navigator && chart.xAxis[0]) {
  3310. extremes = chart.xAxis[0].getExtremes();
  3311. navigator.render(extremes.min, extremes.max);
  3312. }
  3313. });
  3314. }
  3315. H.Navigator = Navigator;
  3316. return H.Navigator;
  3317. });
  3318. _registerModule(_modules, 'Core/Axis/OrdinalAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js'], _modules['Core/Chart/Chart.js']], function (Axis, H, Series, U, Chart) {
  3319. /* *
  3320. *
  3321. * (c) 2010-2021 Torstein Honsi
  3322. *
  3323. * License: www.highcharts.com/license
  3324. *
  3325. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3326. *
  3327. * */
  3328. var addEvent = U.addEvent,
  3329. css = U.css,
  3330. defined = U.defined,
  3331. error = U.error,
  3332. pick = U.pick,
  3333. timeUnits = U.timeUnits;
  3334. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  3335. /**
  3336. * Extends the axis with ordinal support.
  3337. * @private
  3338. */
  3339. var OrdinalAxis;
  3340. (function (OrdinalAxis) {
  3341. /* *
  3342. *
  3343. * Classes
  3344. *
  3345. * */
  3346. /**
  3347. * @private
  3348. */
  3349. var Composition = /** @class */ (function () {
  3350. /* *
  3351. *
  3352. * Constructors
  3353. *
  3354. * */
  3355. /**
  3356. * @private
  3357. */
  3358. function Composition(axis) {
  3359. this.index = {};
  3360. this.axis = axis;
  3361. }
  3362. /* *
  3363. *
  3364. * Functions
  3365. *
  3366. * */
  3367. /**
  3368. * Calculate the ordinal positions before tick positions are calculated.
  3369. *
  3370. * @private
  3371. */
  3372. Composition.prototype.beforeSetTickPositions = function () {
  3373. var axis = this.axis,
  3374. ordinal = axis.ordinal,
  3375. len,
  3376. ordinalPositions = [],
  3377. uniqueOrdinalPositions,
  3378. useOrdinal = false,
  3379. dist,
  3380. extremes = axis.getExtremes(),
  3381. min = extremes.min,
  3382. max = extremes.max,
  3383. minIndex,
  3384. maxIndex,
  3385. slope,
  3386. hasBreaks = axis.isXAxis && !!axis.options.breaks,
  3387. isOrdinal = axis.options.ordinal,
  3388. overscrollPointsRange = Number.MAX_VALUE,
  3389. ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries,
  3390. i,
  3391. hasBoostedSeries;
  3392. // Apply the ordinal logic
  3393. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  3394. axis.series.forEach(function (series, i) {
  3395. uniqueOrdinalPositions = [];
  3396. if ((!ignoreHiddenSeries || series.visible !== false) &&
  3397. (series.takeOrdinalPosition !== false || hasBreaks)) {
  3398. // concatenate the processed X data into the existing
  3399. // positions, or the empty array
  3400. ordinalPositions = ordinalPositions.concat(series.processedXData);
  3401. len = ordinalPositions.length;
  3402. // remove duplicates (#1588)
  3403. ordinalPositions.sort(function (a, b) {
  3404. // without a custom function it is sorted as strings
  3405. return a - b;
  3406. });
  3407. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  3408. // Check for a single-point series:
  3409. series.closestPointRange, overscrollPointsRange));
  3410. if (len) {
  3411. i = 0;
  3412. while (i < len - 1) {
  3413. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  3414. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  3415. }
  3416. i++;
  3417. }
  3418. // Check first item:
  3419. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  3420. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  3421. }
  3422. ordinalPositions = uniqueOrdinalPositions;
  3423. }
  3424. }
  3425. if (series.isSeriesBoosting) {
  3426. hasBoostedSeries = true;
  3427. }
  3428. });
  3429. if (hasBoostedSeries) {
  3430. ordinalPositions.length = 0;
  3431. }
  3432. // cache the length
  3433. len = ordinalPositions.length;
  3434. // Check if we really need the overhead of mapping axis data
  3435. // against the ordinal positions. If the series consist of
  3436. // evenly spaced data any way, we don't need any ordinal logic.
  3437. if (len > 2) { // two points have equal distance by default
  3438. dist = ordinalPositions[1] - ordinalPositions[0];
  3439. i = len - 1;
  3440. while (i-- && !useOrdinal) {
  3441. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  3442. useOrdinal = true;
  3443. }
  3444. }
  3445. // When zooming in on a week, prevent axis padding for
  3446. // weekends even though the data within the week is evenly
  3447. // spaced.
  3448. if (!axis.options.keepOrdinalPadding &&
  3449. (ordinalPositions[0] - min > dist ||
  3450. max - ordinalPositions[ordinalPositions.length - 1] >
  3451. dist)) {
  3452. useOrdinal = true;
  3453. }
  3454. }
  3455. else if (axis.options.overscroll) {
  3456. if (len === 2) {
  3457. // Exactly two points, distance for overscroll is fixed:
  3458. overscrollPointsRange =
  3459. ordinalPositions[1] - ordinalPositions[0];
  3460. }
  3461. else if (len === 1) {
  3462. // We have just one point, closest distance is unknown.
  3463. // Assume then it is last point and overscrolled range:
  3464. overscrollPointsRange = axis.options.overscroll;
  3465. ordinalPositions = [
  3466. ordinalPositions[0],
  3467. ordinalPositions[0] + overscrollPointsRange
  3468. ];
  3469. }
  3470. else {
  3471. // In case of zooming in on overscrolled range, stick to
  3472. // the old range:
  3473. overscrollPointsRange = ordinal.overscrollPointsRange;
  3474. }
  3475. }
  3476. // Record the slope and offset to compute the linear values from
  3477. // the array index. Since the ordinal positions may exceed the
  3478. // current range, get the start and end positions within it
  3479. // (#719, #665b)
  3480. if (useOrdinal) {
  3481. if (axis.options.overscroll) {
  3482. ordinal.overscrollPointsRange = overscrollPointsRange;
  3483. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  3484. }
  3485. // Register
  3486. ordinal.positions = ordinalPositions;
  3487. // This relies on the ordinalPositions being set. Use
  3488. // Math.max and Math.min to prevent padding on either sides
  3489. // of the data.
  3490. minIndex = axis.ordinal2lin(// #5979
  3491. Math.max(min, ordinalPositions[0]), true);
  3492. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  3493. // Set the slope and offset of the values compared to the
  3494. // indices in the ordinal positions
  3495. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  3496. ordinal.offset = min - (minIndex * slope);
  3497. }
  3498. else {
  3499. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  3500. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  3501. void 0;
  3502. }
  3503. }
  3504. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  3505. ordinal.groupIntervalFactor = null; // reset for next run
  3506. };
  3507. /**
  3508. * Get the ordinal positions for the entire data set. This is necessary
  3509. * in chart panning because we need to find out what points or data
  3510. * groups are available outside the visible range. When a panning
  3511. * operation starts, if an index for the given grouping does not exists,
  3512. * it is created and cached. This index is deleted on updated data, so
  3513. * it will be regenerated the next time a panning operation starts.
  3514. *
  3515. * @private
  3516. */
  3517. Composition.prototype.getExtendedPositions = function () {
  3518. var ordinal = this,
  3519. axis = ordinal.axis,
  3520. axisProto = axis.constructor.prototype,
  3521. chart = axis.chart,
  3522. grouping = axis.series[0].currentDataGrouping,
  3523. ordinalIndex = ordinal.index,
  3524. key = grouping ?
  3525. grouping.count + grouping.unitName :
  3526. 'raw',
  3527. overscroll = axis.options.overscroll,
  3528. extremes = axis.getExtremes(),
  3529. fakeAxis,
  3530. fakeSeries;
  3531. // If this is the first time, or the ordinal index is deleted by
  3532. // updatedData,
  3533. // create it.
  3534. if (!ordinalIndex) {
  3535. ordinalIndex = ordinal.index = {};
  3536. }
  3537. if (!ordinalIndex[key]) {
  3538. // Create a fake axis object where the extended ordinal
  3539. // positions are emulated
  3540. fakeAxis = {
  3541. series: [],
  3542. chart: chart,
  3543. getExtremes: function () {
  3544. return {
  3545. min: extremes.dataMin,
  3546. max: extremes.dataMax + overscroll
  3547. };
  3548. },
  3549. options: {
  3550. ordinal: true
  3551. },
  3552. ordinal: {},
  3553. ordinal2lin: axisProto.ordinal2lin,
  3554. val2lin: axisProto.val2lin // #2590
  3555. };
  3556. fakeAxis.ordinal.axis = fakeAxis;
  3557. // Add the fake series to hold the full data, then apply
  3558. // processData to it
  3559. axis.series.forEach(function (series) {
  3560. fakeSeries = {
  3561. xAxis: fakeAxis,
  3562. xData: series.xData.slice(),
  3563. chart: chart,
  3564. destroyGroupedData: H.noop,
  3565. getProcessedData: Series.prototype.getProcessedData
  3566. };
  3567. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  3568. fakeSeries.options = {
  3569. dataGrouping: grouping ? {
  3570. enabled: true,
  3571. forced: true,
  3572. // doesn't matter which, use the fastest
  3573. approximation: 'open',
  3574. units: [[
  3575. grouping.unitName,
  3576. [grouping.count]
  3577. ]]
  3578. } : {
  3579. enabled: false
  3580. }
  3581. };
  3582. series.processData.apply(fakeSeries);
  3583. fakeAxis.series.push(fakeSeries);
  3584. });
  3585. // Run beforeSetTickPositions to compute the ordinalPositions
  3586. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  3587. // Cache it
  3588. ordinalIndex[key] = fakeAxis.ordinal.positions;
  3589. }
  3590. return ordinalIndex[key];
  3591. };
  3592. /**
  3593. * Find the factor to estimate how wide the plot area would have been if
  3594. * ordinal gaps were included. This value is used to compute an imagined
  3595. * plot width in order to establish the data grouping interval.
  3596. *
  3597. * A real world case is the intraday-candlestick example. Without this
  3598. * logic, it would show the correct data grouping when viewing a range
  3599. * within each day, but once moving the range to include the gap between
  3600. * two days, the interval would include the cut-away night hours and the
  3601. * data grouping would be wrong. So the below method tries to compensate
  3602. * by identifying the most common point interval, in this case days.
  3603. *
  3604. * An opposite case is presented in issue #718. We have a long array of
  3605. * daily data, then one point is appended one hour after the last point.
  3606. * We expect the data grouping not to change.
  3607. *
  3608. * In the future, if we find cases where this estimation doesn't work
  3609. * optimally, we might need to add a second pass to the data grouping
  3610. * logic, where we do another run with a greater interval if the number
  3611. * of data groups is more than a certain fraction of the desired group
  3612. * count.
  3613. *
  3614. * @private
  3615. */
  3616. Composition.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
  3617. var ordinal = this,
  3618. axis = ordinal.axis,
  3619. i,
  3620. processedXData = series.processedXData,
  3621. len = processedXData.length,
  3622. distances = [],
  3623. median,
  3624. groupIntervalFactor = ordinal.groupIntervalFactor;
  3625. // Only do this computation for the first series, let the other
  3626. // inherit it (#2416)
  3627. if (!groupIntervalFactor) {
  3628. // Register all the distances in an array
  3629. for (i = 0; i < len - 1; i++) {
  3630. distances[i] =
  3631. processedXData[i + 1] - processedXData[i];
  3632. }
  3633. // Sort them and find the median
  3634. distances.sort(function (a, b) {
  3635. return a - b;
  3636. });
  3637. median = distances[Math.floor(len / 2)];
  3638. // Compensate for series that don't extend through the entire
  3639. // axis extent. #1675.
  3640. xMin = Math.max(xMin, processedXData[0]);
  3641. xMax = Math.min(xMax, processedXData[len - 1]);
  3642. ordinal.groupIntervalFactor = groupIntervalFactor =
  3643. (len * median) / (xMax - xMin);
  3644. }
  3645. // Return the factor needed for data grouping
  3646. return groupIntervalFactor;
  3647. };
  3648. /**
  3649. * Get ticks for an ordinal axis within a range where points don't
  3650. * exist. It is required when overscroll is enabled. We can't base on
  3651. * points, because we may not have any, so we use approximated
  3652. * pointRange and generate these ticks between Axis.dataMax,
  3653. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  3654. * navigator scrolling.
  3655. *
  3656. * @private
  3657. */
  3658. Composition.prototype.getOverscrollPositions = function () {
  3659. var ordinal = this,
  3660. axis = ordinal.axis,
  3661. extraRange = axis.options.overscroll,
  3662. distance = ordinal.overscrollPointsRange,
  3663. positions = [],
  3664. max = axis.dataMax;
  3665. if (defined(distance)) {
  3666. // Max + pointRange because we need to scroll to the last
  3667. positions.push(max);
  3668. while (max <= axis.dataMax + extraRange) {
  3669. max += distance;
  3670. positions.push(max);
  3671. }
  3672. }
  3673. return positions;
  3674. };
  3675. /**
  3676. * Make the tick intervals closer because the ordinal gaps make the
  3677. * ticks spread out or cluster.
  3678. *
  3679. * @private
  3680. */
  3681. Composition.prototype.postProcessTickInterval = function (tickInterval) {
  3682. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  3683. // This is a case where this algorithm doesn't work optimally. In
  3684. // this case, the tick labels are spread out per week, but all the
  3685. // gaps reside within weeks. So we have a situation where the labels
  3686. // are courser than the ordinal gaps, and thus the tick interval
  3687. // should not be altered.
  3688. var ordinal = this,
  3689. axis = ordinal.axis,
  3690. ordinalSlope = ordinal.slope,
  3691. ret;
  3692. if (ordinalSlope) {
  3693. if (!axis.options.breaks) {
  3694. ret = tickInterval / (ordinalSlope / axis.closestPointRange);
  3695. }
  3696. else {
  3697. ret = axis.closestPointRange || tickInterval; // #7275
  3698. }
  3699. }
  3700. else {
  3701. ret = tickInterval;
  3702. }
  3703. return ret;
  3704. };
  3705. return Composition;
  3706. }());
  3707. OrdinalAxis.Composition = Composition;
  3708. /* *
  3709. *
  3710. * Functions
  3711. *
  3712. * */
  3713. /**
  3714. * Extends the axis with ordinal support.
  3715. *
  3716. * @private
  3717. *
  3718. * @param AxisClass
  3719. * Axis class to extend.
  3720. *
  3721. * @param ChartClass
  3722. * Chart class to use.
  3723. *
  3724. * @param SeriesClass
  3725. * Series class to use.
  3726. */
  3727. function compose(AxisClass, ChartClass, SeriesClass) {
  3728. AxisClass.keepProps.push('ordinal');
  3729. var axisProto = AxisClass.prototype;
  3730. /**
  3731. * In an ordinal axis, there might be areas with dense consentrations of
  3732. * points, then large gaps between some. Creating equally distributed
  3733. * ticks over this entire range may lead to a huge number of ticks that
  3734. * will later be removed. So instead, break the positions up in
  3735. * segments, find the tick positions for each segment then concatenize
  3736. * them. This method is used from both data grouping logic and X axis
  3737. * tick position logic.
  3738. *
  3739. * @private
  3740. */
  3741. AxisClass.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  3742. if (positions === void 0) { positions = []; }
  3743. if (closestDistance === void 0) { closestDistance = 0; }
  3744. var start = 0,
  3745. end,
  3746. segmentPositions,
  3747. higherRanks = {},
  3748. hasCrossedHigherRank,
  3749. info,
  3750. posLength,
  3751. outsideMax,
  3752. groupPositions = [],
  3753. lastGroupPosition = -Number.MAX_VALUE,
  3754. tickPixelIntervalOption = this.options.tickPixelInterval,
  3755. time = this.chart.time,
  3756. // Record all the start positions of a segment, to use when
  3757. // deciding what's a gap in the data.
  3758. segmentStarts = [];
  3759. // The positions are not always defined, for example for ordinal
  3760. // positions when data has regular interval (#1557, #2090)
  3761. if ((!this.options.ordinal && !this.options.breaks) ||
  3762. !positions ||
  3763. positions.length < 3 ||
  3764. typeof min === 'undefined') {
  3765. return time.getTimeTicks.apply(time, arguments);
  3766. }
  3767. // Analyze the positions array to split it into segments on gaps
  3768. // larger than 5 times the closest distance. The closest distance is
  3769. // already found at this point, so we reuse that instead of
  3770. // computing it again.
  3771. posLength = positions.length;
  3772. for (end = 0; end < posLength; end++) {
  3773. outsideMax = end && positions[end - 1] > max;
  3774. if (positions[end] < min) { // Set the last position before min
  3775. start = end;
  3776. }
  3777. if (end === posLength - 1 ||
  3778. positions[end + 1] - positions[end] > closestDistance * 5 ||
  3779. outsideMax) {
  3780. // For each segment, calculate the tick positions from the
  3781. // getTimeTicks utility function. The interval will be the
  3782. // same regardless of how long the segment is.
  3783. if (positions[end] > lastGroupPosition) { // #1475
  3784. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  3785. // Prevent duplicate groups, for example for multiple
  3786. // segments within one larger time frame (#1475)
  3787. while (segmentPositions.length &&
  3788. segmentPositions[0] <= lastGroupPosition) {
  3789. segmentPositions.shift();
  3790. }
  3791. if (segmentPositions.length) {
  3792. lastGroupPosition =
  3793. segmentPositions[segmentPositions.length - 1];
  3794. }
  3795. segmentStarts.push(groupPositions.length);
  3796. groupPositions = groupPositions.concat(segmentPositions);
  3797. }
  3798. // Set start of next segment
  3799. start = end + 1;
  3800. }
  3801. if (outsideMax) {
  3802. break;
  3803. }
  3804. }
  3805. // Get the grouping info from the last of the segments. The info is
  3806. // the same for all segments.
  3807. if (segmentPositions) {
  3808. info = segmentPositions.info;
  3809. // Optionally identify ticks with higher rank, for example
  3810. // when the ticks have crossed midnight.
  3811. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  3812. end = groupPositions.length - 1;
  3813. // Compare points two by two
  3814. for (start = 1; start < end; start++) {
  3815. if (time.dateFormat('%d', groupPositions[start]) !==
  3816. time.dateFormat('%d', groupPositions[start - 1])) {
  3817. higherRanks[groupPositions[start]] = 'day';
  3818. hasCrossedHigherRank = true;
  3819. }
  3820. }
  3821. // If the complete array has crossed midnight, we want
  3822. // to mark the first positions also as higher rank
  3823. if (hasCrossedHigherRank) {
  3824. higherRanks[groupPositions[0]] = 'day';
  3825. }
  3826. info.higherRanks = higherRanks;
  3827. }
  3828. // Save the info
  3829. info.segmentStarts = segmentStarts;
  3830. groupPositions.info = info;
  3831. }
  3832. else {
  3833. error(12, false, this.chart);
  3834. }
  3835. // Don't show ticks within a gap in the ordinal axis, where the
  3836. // space between two points is greater than a portion of the tick
  3837. // pixel interval
  3838. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  3839. var length = groupPositions.length,
  3840. i = length,
  3841. itemToRemove,
  3842. translated,
  3843. translatedArr = [],
  3844. lastTranslated,
  3845. medianDistance,
  3846. distance,
  3847. distances = [];
  3848. // Find median pixel distance in order to keep a reasonably even
  3849. // distance between ticks (#748)
  3850. while (i--) {
  3851. translated = this.translate(groupPositions[i]);
  3852. if (lastTranslated) {
  3853. distances[i] = lastTranslated - translated;
  3854. }
  3855. translatedArr[i] = lastTranslated = translated;
  3856. }
  3857. distances.sort();
  3858. medianDistance = distances[Math.floor(distances.length / 2)];
  3859. if (medianDistance < tickPixelIntervalOption * 0.6) {
  3860. medianDistance = null;
  3861. }
  3862. // Now loop over again and remove ticks where needed
  3863. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  3864. lastTranslated = void 0;
  3865. while (i--) {
  3866. translated = translatedArr[i];
  3867. distance = Math.abs(lastTranslated - translated);
  3868. // #4175 - when axis is reversed, the distance, is negative
  3869. // but tickPixelIntervalOption positive, so we need to
  3870. // compare the same values
  3871. // Remove ticks that are closer than 0.6 times the pixel
  3872. // interval from the one to the right, but not if it is
  3873. // close to the median distance (#748).
  3874. if (lastTranslated &&
  3875. distance < tickPixelIntervalOption * 0.8 &&
  3876. (medianDistance === null || distance < medianDistance * 0.8)) {
  3877. // Is this a higher ranked position with a normal
  3878. // position to the right?
  3879. if (higherRanks[groupPositions[i]] &&
  3880. !higherRanks[groupPositions[i + 1]]) {
  3881. // Yes: remove the lower ranked neighbour to the
  3882. // right
  3883. itemToRemove = i + 1;
  3884. lastTranslated = translated; // #709
  3885. }
  3886. else {
  3887. // No: remove this one
  3888. itemToRemove = i;
  3889. }
  3890. groupPositions.splice(itemToRemove, 1);
  3891. }
  3892. else {
  3893. lastTranslated = translated;
  3894. }
  3895. }
  3896. }
  3897. return groupPositions;
  3898. };
  3899. /**
  3900. * Translate from linear (internal) to axis value.
  3901. *
  3902. * @private
  3903. * @function Highcharts.Axis#lin2val
  3904. *
  3905. * @param {number} val
  3906. * The linear abstracted value.
  3907. *
  3908. * @param {boolean} [fromIndex]
  3909. * Translate from an index in the ordinal positions rather than a
  3910. * value.
  3911. *
  3912. * @return {number}
  3913. */
  3914. axisProto.lin2val = function (val, fromIndex) {
  3915. var axis = this,
  3916. ordinal = axis.ordinal,
  3917. ordinalPositions = ordinal.positions,
  3918. ret;
  3919. // the visible range contains only equally spaced values
  3920. if (!ordinalPositions) {
  3921. ret = val;
  3922. }
  3923. else {
  3924. var ordinalSlope = ordinal.slope,
  3925. ordinalOffset = ordinal.offset,
  3926. i = ordinalPositions.length - 1,
  3927. linearEquivalentLeft,
  3928. linearEquivalentRight,
  3929. distance;
  3930. // Handle the case where we translate from the index directly,
  3931. // used only when panning an ordinal axis
  3932. if (fromIndex) {
  3933. if (val < 0) { // out of range, in effect panning to the left
  3934. val = ordinalPositions[0];
  3935. }
  3936. else if (val > i) { // out of range, panning to the right
  3937. val = ordinalPositions[i];
  3938. }
  3939. else { // split it up
  3940. i = Math.floor(val);
  3941. distance = val - i; // the decimal
  3942. }
  3943. // Loop down along the ordinal positions. When the linear
  3944. // equivalent of i matches an ordinal position, interpolate
  3945. // between the left and right values.
  3946. }
  3947. else {
  3948. while (i--) {
  3949. linearEquivalentLeft =
  3950. (ordinalSlope * i) + ordinalOffset;
  3951. if (val >= linearEquivalentLeft) {
  3952. linearEquivalentRight =
  3953. (ordinalSlope *
  3954. (i + 1)) +
  3955. ordinalOffset;
  3956. // something between 0 and 1
  3957. distance = (val - linearEquivalentLeft) /
  3958. (linearEquivalentRight - linearEquivalentLeft);
  3959. break;
  3960. }
  3961. }
  3962. }
  3963. // If the index is within the range of the ordinal positions,
  3964. // return the associated or interpolated value. If not, just
  3965. // return the value.
  3966. return (typeof distance !== 'undefined' &&
  3967. typeof ordinalPositions[i] !== 'undefined' ?
  3968. ordinalPositions[i] + (distance ?
  3969. distance *
  3970. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  3971. 0) :
  3972. val);
  3973. }
  3974. return ret;
  3975. };
  3976. /**
  3977. * Translate from a linear axis value to the corresponding ordinal axis
  3978. * position. If there are no gaps in the ordinal axis this will be the
  3979. * same. The translated value is the value that the point would have if
  3980. * the axis were linear, using the same min and max.
  3981. *
  3982. * @private
  3983. * @function Highcharts.Axis#val2lin
  3984. *
  3985. * @param {number} val
  3986. * The axis value.
  3987. *
  3988. * @param {boolean} [toIndex]
  3989. * Whether to return the index in the ordinalPositions or the new value.
  3990. *
  3991. * @return {number}
  3992. */
  3993. axisProto.val2lin = function (val, toIndex) {
  3994. var axis = this,
  3995. ordinal = axis.ordinal,
  3996. ordinalPositions = ordinal.positions,
  3997. ret;
  3998. if (!ordinalPositions) {
  3999. ret = val;
  4000. }
  4001. else {
  4002. var ordinalLength = ordinalPositions.length,
  4003. i,
  4004. distance,
  4005. ordinalIndex;
  4006. // first look for an exact match in the ordinalpositions array
  4007. i = ordinalLength;
  4008. while (i--) {
  4009. if (ordinalPositions[i] === val) {
  4010. ordinalIndex = i;
  4011. break;
  4012. }
  4013. }
  4014. // if that failed, find the intermediate position between the
  4015. // two nearest values
  4016. i = ordinalLength - 1;
  4017. while (i--) {
  4018. if (val > ordinalPositions[i] || i === 0) { // interpolate
  4019. // something between 0 and 1
  4020. distance = (val - ordinalPositions[i]) /
  4021. (ordinalPositions[i + 1] - ordinalPositions[i]);
  4022. ordinalIndex = i + distance;
  4023. break;
  4024. }
  4025. }
  4026. ret = toIndex ?
  4027. ordinalIndex :
  4028. ordinal.slope *
  4029. (ordinalIndex || 0) +
  4030. ordinal.offset;
  4031. }
  4032. return ret;
  4033. };
  4034. // Record this to prevent overwriting by broken-axis module (#5979)
  4035. axisProto.ordinal2lin = axisProto.val2lin;
  4036. /* eslint-disable no-invalid-this */
  4037. addEvent(AxisClass, 'afterInit', function () {
  4038. var axis = this;
  4039. if (!axis.ordinal) {
  4040. axis.ordinal = new OrdinalAxis.Composition(axis);
  4041. }
  4042. });
  4043. addEvent(AxisClass, 'foundExtremes', function () {
  4044. var axis = this;
  4045. if (axis.isXAxis &&
  4046. defined(axis.options.overscroll) &&
  4047. axis.max === axis.dataMax &&
  4048. (
  4049. // Panning is an execption. We don't want to apply
  4050. // overscroll when panning over the dataMax
  4051. !axis.chart.mouseIsDown ||
  4052. axis.isInternal) && (
  4053. // Scrollbar buttons are the other execption:
  4054. !axis.eventArgs ||
  4055. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  4056. axis.max += axis.options.overscroll;
  4057. // Live data and buttons require translation for the min:
  4058. if (!axis.isInternal && defined(axis.userMin)) {
  4059. axis.min += axis.options.overscroll;
  4060. }
  4061. }
  4062. });
  4063. // For ordinal axis, that loads data async, redraw axis after data is
  4064. // loaded. If we don't do that, axis will have the same extremes as
  4065. // previously, but ordinal positions won't be calculated. See #10290
  4066. addEvent(AxisClass, 'afterSetScale', function () {
  4067. var axis = this;
  4068. if (axis.horiz && !axis.isDirty) {
  4069. axis.isDirty = axis.isOrdinal &&
  4070. axis.chart.navigator &&
  4071. !axis.chart.navigator.adaptToUpdatedData;
  4072. }
  4073. });
  4074. addEvent(AxisClass, 'initialAxisTranslation', function () {
  4075. var axis = this;
  4076. if (axis.ordinal) {
  4077. axis.ordinal.beforeSetTickPositions();
  4078. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  4079. }
  4080. });
  4081. // Extending the Chart.pan method for ordinal axes
  4082. addEvent(ChartClass, 'pan', function (e) {
  4083. var chart = this,
  4084. xAxis = chart.xAxis[0],
  4085. overscroll = xAxis.options.overscroll,
  4086. chartX = e.originalEvent.chartX,
  4087. panning = chart.options.chart &&
  4088. chart.options.chart.panning,
  4089. runBase = false;
  4090. if (panning &&
  4091. panning.type !== 'y' &&
  4092. xAxis.options.ordinal &&
  4093. xAxis.series.length) {
  4094. var mouseDownX = chart.mouseDownX,
  4095. extremes = xAxis.getExtremes(),
  4096. dataMax = extremes.dataMax,
  4097. min = extremes.min,
  4098. max = extremes.max,
  4099. trimmedRange,
  4100. hoverPoints = chart.hoverPoints,
  4101. closestPointRange = (xAxis.closestPointRange ||
  4102. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)),
  4103. pointPixelWidth = (xAxis.translationSlope *
  4104. (xAxis.ordinal.slope || closestPointRange)),
  4105. // how many ordinal units did we move?
  4106. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  4107. // get index of all the chart's points
  4108. extendedAxis = { ordinal: { positions: xAxis.ordinal.getExtendedPositions() } },
  4109. ordinalPositions,
  4110. searchAxisLeft,
  4111. lin2val = xAxis.lin2val,
  4112. val2lin = xAxis.val2lin,
  4113. searchAxisRight;
  4114. // we have an ordinal axis, but the data is equally spaced
  4115. if (!extendedAxis.ordinal.positions) {
  4116. runBase = true;
  4117. }
  4118. else if (Math.abs(movedUnits) > 1) {
  4119. // Remove active points for shared tooltip
  4120. if (hoverPoints) {
  4121. hoverPoints.forEach(function (point) {
  4122. point.setState();
  4123. });
  4124. }
  4125. if (movedUnits < 0) {
  4126. searchAxisLeft = extendedAxis;
  4127. searchAxisRight = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4128. }
  4129. else {
  4130. searchAxisLeft = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4131. searchAxisRight = extendedAxis;
  4132. }
  4133. // In grouped data series, the last ordinal position
  4134. // represents the grouped data, which is to the left of the
  4135. // real data max. If we don't compensate for this, we will
  4136. // be allowed to pan grouped data series passed the right of
  4137. // the plot area.
  4138. ordinalPositions = searchAxisRight.ordinal.positions;
  4139. if (dataMax >
  4140. ordinalPositions[ordinalPositions.length - 1]) {
  4141. ordinalPositions.push(dataMax);
  4142. }
  4143. // Get the new min and max values by getting the ordinal
  4144. // index for the current extreme, then add the moved units
  4145. // and translate back to values. This happens on the
  4146. // extended ordinal positions if the new position is out of
  4147. // range, else it happens on the current x axis which is
  4148. // smaller and faster.
  4149. chart.fixedRange = max - min;
  4150. trimmedRange = xAxis.navigatorAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  4151. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  4152. true // translate from index
  4153. ]), lin2val.apply(searchAxisRight, [
  4154. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  4155. true // translate from index
  4156. ]));
  4157. // Apply it if it is within the available data range
  4158. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  4159. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  4160. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  4161. }
  4162. chart.mouseDownX = chartX; // set new reference for next run
  4163. css(chart.container, { cursor: 'move' });
  4164. }
  4165. }
  4166. else {
  4167. runBase = true;
  4168. }
  4169. // revert to the linear chart.pan version
  4170. if (runBase || (panning && /y/.test(panning.type))) {
  4171. if (overscroll) {
  4172. xAxis.max = xAxis.dataMax + overscroll;
  4173. }
  4174. }
  4175. else {
  4176. e.preventDefault();
  4177. }
  4178. });
  4179. addEvent(SeriesClass, 'updatedData', function () {
  4180. var xAxis = this.xAxis;
  4181. // Destroy the extended ordinal index on updated data
  4182. if (xAxis && xAxis.options.ordinal) {
  4183. delete xAxis.ordinal.index;
  4184. }
  4185. });
  4186. /* eslint-enable no-invalid-this */
  4187. }
  4188. OrdinalAxis.compose = compose;
  4189. })(OrdinalAxis || (OrdinalAxis = {}));
  4190. OrdinalAxis.compose(Axis, Chart, Series); // @todo move to StockChart, remove from master
  4191. return OrdinalAxis;
  4192. });
  4193. _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) {
  4194. /* *
  4195. *
  4196. * (c) 2009-2021 Torstein Honsi
  4197. *
  4198. * License: www.highcharts.com/license
  4199. *
  4200. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4201. *
  4202. * */
  4203. var addEvent = U.addEvent,
  4204. find = U.find,
  4205. fireEvent = U.fireEvent,
  4206. isArray = U.isArray,
  4207. isNumber = U.isNumber,
  4208. pick = U.pick;
  4209. /* eslint-disable valid-jsdoc */
  4210. /**
  4211. * Provides support for broken axes.
  4212. * @private
  4213. * @class
  4214. */
  4215. var BrokenAxisAdditions = /** @class */ (function () {
  4216. /* *
  4217. *
  4218. * Constructors
  4219. *
  4220. * */
  4221. function BrokenAxisAdditions(axis) {
  4222. this.hasBreaks = false;
  4223. this.axis = axis;
  4224. }
  4225. /* *
  4226. *
  4227. * Static Functions
  4228. *
  4229. * */
  4230. /**
  4231. * @private
  4232. */
  4233. BrokenAxisAdditions.isInBreak = function (brk, val) {
  4234. var ret,
  4235. repeat = brk.repeat || Infinity,
  4236. from = brk.from,
  4237. length = brk.to - brk.from,
  4238. test = (val >= from ?
  4239. (val - from) % repeat :
  4240. repeat - ((from - val) % repeat));
  4241. if (!brk.inclusive) {
  4242. ret = test < length && test !== 0;
  4243. }
  4244. else {
  4245. ret = test <= length;
  4246. }
  4247. return ret;
  4248. };
  4249. /**
  4250. * @private
  4251. */
  4252. BrokenAxisAdditions.lin2Val = function (val) {
  4253. var axis = this;
  4254. var brokenAxis = axis.brokenAxis;
  4255. var breakArray = brokenAxis && brokenAxis.breakArray;
  4256. if (!breakArray) {
  4257. return val;
  4258. }
  4259. var nval = val,
  4260. brk,
  4261. i;
  4262. for (i = 0; i < breakArray.length; i++) {
  4263. brk = breakArray[i];
  4264. if (brk.from >= nval) {
  4265. break;
  4266. }
  4267. else if (brk.to < nval) {
  4268. nval += brk.len;
  4269. }
  4270. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  4271. nval += brk.len;
  4272. }
  4273. }
  4274. return nval;
  4275. };
  4276. /**
  4277. * @private
  4278. */
  4279. BrokenAxisAdditions.val2Lin = function (val) {
  4280. var axis = this;
  4281. var brokenAxis = axis.brokenAxis;
  4282. var breakArray = brokenAxis && brokenAxis.breakArray;
  4283. if (!breakArray) {
  4284. return val;
  4285. }
  4286. var nval = val,
  4287. brk,
  4288. i;
  4289. for (i = 0; i < breakArray.length; i++) {
  4290. brk = breakArray[i];
  4291. if (brk.to <= val) {
  4292. nval -= brk.len;
  4293. }
  4294. else if (brk.from >= val) {
  4295. break;
  4296. }
  4297. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  4298. nval -= (val - brk.from);
  4299. break;
  4300. }
  4301. }
  4302. return nval;
  4303. };
  4304. /* *
  4305. *
  4306. * Functions
  4307. *
  4308. * */
  4309. /**
  4310. * Returns the first break found where the x is larger then break.from and
  4311. * smaller then break.to.
  4312. *
  4313. * @param {number} x
  4314. * The number which should be within a break.
  4315. *
  4316. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  4317. * The array of breaks to search within.
  4318. *
  4319. * @return {Highcharts.XAxisBreaksOptions|undefined}
  4320. * Returns the first break found that matches, returns false if no break is
  4321. * found.
  4322. */
  4323. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  4324. return find(breaks, function (b) {
  4325. return b.from < x && x < b.to;
  4326. });
  4327. };
  4328. /**
  4329. * @private
  4330. */
  4331. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  4332. var brokenAxis = this;
  4333. var axis = brokenAxis.axis;
  4334. var breaks = axis.options.breaks,
  4335. i = breaks && breaks.length,
  4336. inbrk,
  4337. keep,
  4338. ret;
  4339. if (i) {
  4340. while (i--) {
  4341. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  4342. inbrk = true;
  4343. if (!keep) {
  4344. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  4345. }
  4346. }
  4347. }
  4348. if (inbrk && testKeep) {
  4349. ret = inbrk && !keep;
  4350. }
  4351. else {
  4352. ret = inbrk;
  4353. }
  4354. }
  4355. return ret;
  4356. };
  4357. /**
  4358. * Dynamically set or unset breaks in an axis. This function in lighter than
  4359. * usin Axis.update, and it also preserves animation.
  4360. *
  4361. * @private
  4362. * @function Highcharts.Axis#setBreaks
  4363. *
  4364. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  4365. * The breaks to add. When `undefined` it removes existing breaks.
  4366. *
  4367. * @param {boolean} [redraw=true]
  4368. * Whether to redraw the chart immediately.
  4369. *
  4370. * @return {void}
  4371. */
  4372. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  4373. var brokenAxis = this;
  4374. var axis = brokenAxis.axis;
  4375. var hasBreaks = (isArray(breaks) && !!breaks.length);
  4376. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  4377. brokenAxis.hasBreaks = hasBreaks;
  4378. axis.options.breaks = axis.userOptions.breaks = breaks;
  4379. axis.forceRedraw = true; // Force recalculation in setScale
  4380. // Recalculate series related to the axis.
  4381. axis.series.forEach(function (series) {
  4382. series.isDirty = true;
  4383. });
  4384. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  4385. // Revert to prototype functions
  4386. delete axis.val2lin;
  4387. delete axis.lin2val;
  4388. }
  4389. if (hasBreaks) {
  4390. axis.userOptions.ordinal = false;
  4391. axis.lin2val = BrokenAxisAdditions.lin2Val;
  4392. axis.val2lin = BrokenAxisAdditions.val2Lin;
  4393. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  4394. // If trying to set extremes inside a break, extend min to
  4395. // after, and max to before the break ( #3857 )
  4396. if (brokenAxis.hasBreaks) {
  4397. var axisBreak,
  4398. breaks = this.options.breaks;
  4399. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  4400. newMin = axisBreak.to;
  4401. }
  4402. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  4403. newMax = axisBreak.from;
  4404. }
  4405. // If both min and max is within the same break.
  4406. if (newMax < newMin) {
  4407. newMax = newMin;
  4408. }
  4409. }
  4410. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  4411. };
  4412. axis.setAxisTranslation = function () {
  4413. Axis.prototype.setAxisTranslation.call(this);
  4414. brokenAxis.unitLength = null;
  4415. if (brokenAxis.hasBreaks) {
  4416. var breaks = axis.options.breaks || [],
  4417. // Temporary one:
  4418. breakArrayT = [],
  4419. breakArray = [],
  4420. length = 0,
  4421. inBrk,
  4422. repeat,
  4423. min = axis.userMin || axis.min,
  4424. max = axis.userMax || axis.max,
  4425. pointRangePadding = pick(axis.pointRangePadding, 0),
  4426. start,
  4427. i;
  4428. // Min & max check (#4247)
  4429. breaks.forEach(function (brk) {
  4430. repeat = brk.repeat || Infinity;
  4431. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  4432. min +=
  4433. (brk.to % repeat) -
  4434. (min % repeat);
  4435. }
  4436. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  4437. max -=
  4438. (max % repeat) -
  4439. (brk.from % repeat);
  4440. }
  4441. });
  4442. // Construct an array holding all breaks in the axis
  4443. breaks.forEach(function (brk) {
  4444. start = brk.from;
  4445. repeat = brk.repeat || Infinity;
  4446. while (start - repeat > min) {
  4447. start -= repeat;
  4448. }
  4449. while (start < min) {
  4450. start += repeat;
  4451. }
  4452. for (i = start; i < max; i += repeat) {
  4453. breakArrayT.push({
  4454. value: i,
  4455. move: 'in'
  4456. });
  4457. breakArrayT.push({
  4458. value: i + (brk.to - brk.from),
  4459. move: 'out',
  4460. size: brk.breakSize
  4461. });
  4462. }
  4463. });
  4464. breakArrayT.sort(function (a, b) {
  4465. return ((a.value === b.value) ?
  4466. ((a.move === 'in' ? 0 : 1) -
  4467. (b.move === 'in' ? 0 : 1)) :
  4468. a.value - b.value);
  4469. });
  4470. // Simplify the breaks
  4471. inBrk = 0;
  4472. start = min;
  4473. breakArrayT.forEach(function (brk) {
  4474. inBrk += (brk.move === 'in' ? 1 : -1);
  4475. if (inBrk === 1 && brk.move === 'in') {
  4476. start = brk.value;
  4477. }
  4478. if (inBrk === 0) {
  4479. breakArray.push({
  4480. from: start,
  4481. to: brk.value,
  4482. len: brk.value - start - (brk.size || 0)
  4483. });
  4484. length += brk.value - start - (brk.size || 0);
  4485. }
  4486. });
  4487. /**
  4488. * HC <= 8 backwards compatibility, used by demo samples.
  4489. * @deprecated
  4490. * @private
  4491. * @requires modules/broken-axis
  4492. */
  4493. axis.breakArray = brokenAxis.breakArray = breakArray;
  4494. // Used with staticScale, and below the actual axis length,
  4495. // when breaks are substracted.
  4496. brokenAxis.unitLength = max - min - length + pointRangePadding;
  4497. fireEvent(axis, 'afterBreaks');
  4498. if (axis.staticScale) {
  4499. axis.transA = axis.staticScale;
  4500. }
  4501. else if (brokenAxis.unitLength) {
  4502. axis.transA *=
  4503. (max - axis.min + pointRangePadding) /
  4504. brokenAxis.unitLength;
  4505. }
  4506. if (pointRangePadding) {
  4507. axis.minPixelPadding =
  4508. axis.transA * axis.minPointOffset;
  4509. }
  4510. axis.min = min;
  4511. axis.max = max;
  4512. }
  4513. };
  4514. }
  4515. if (pick(redraw, true)) {
  4516. axis.chart.redraw();
  4517. }
  4518. };
  4519. return BrokenAxisAdditions;
  4520. }());
  4521. /**
  4522. * Axis with support of broken data rows.
  4523. * @private
  4524. * @class
  4525. */
  4526. var BrokenAxis = /** @class */ (function () {
  4527. function BrokenAxis() {
  4528. }
  4529. /**
  4530. * Adds support for broken axes.
  4531. * @private
  4532. */
  4533. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  4534. AxisClass.keepProps.push('brokenAxis');
  4535. var seriesProto = Series.prototype;
  4536. /**
  4537. * @private
  4538. */
  4539. seriesProto.drawBreaks = function (axis, keys) {
  4540. var series = this,
  4541. points = series.points,
  4542. breaks,
  4543. threshold,
  4544. eventName,
  4545. y;
  4546. if (axis && // #5950
  4547. axis.brokenAxis &&
  4548. axis.brokenAxis.hasBreaks) {
  4549. var brokenAxis_1 = axis.brokenAxis;
  4550. keys.forEach(function (key) {
  4551. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  4552. threshold = axis.isXAxis ?
  4553. axis.min :
  4554. pick(series.options.threshold, axis.min);
  4555. points.forEach(function (point) {
  4556. y = pick(point['stack' + key.toUpperCase()], point[key]);
  4557. breaks.forEach(function (brk) {
  4558. if (isNumber(threshold) && isNumber(y)) {
  4559. eventName = false;
  4560. if ((threshold < brk.from && y > brk.to) ||
  4561. (threshold > brk.from && y < brk.from)) {
  4562. eventName = 'pointBreak';
  4563. }
  4564. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  4565. (threshold > brk.from && y > brk.to && y < brk.from)) {
  4566. eventName = 'pointInBreak';
  4567. }
  4568. if (eventName) {
  4569. fireEvent(axis, eventName, { point: point, brk: brk });
  4570. }
  4571. }
  4572. });
  4573. });
  4574. });
  4575. }
  4576. };
  4577. /**
  4578. * Extend getGraphPath by identifying gaps in the data so that we can
  4579. * draw a gap in the line or area. This was moved from ordinal axis
  4580. * module to broken axis module as of #5045.
  4581. *
  4582. * @private
  4583. * @function Highcharts.Series#gappedPath
  4584. *
  4585. * @return {Highcharts.SVGPathArray}
  4586. * Gapped path
  4587. */
  4588. seriesProto.gappedPath = function () {
  4589. var currentDataGrouping = this.currentDataGrouping,
  4590. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  4591. gapSize = this.options.gapSize,
  4592. points = this.points.slice(),
  4593. i = points.length - 1,
  4594. yAxis = this.yAxis,
  4595. stack;
  4596. /**
  4597. * Defines when to display a gap in the graph, together with the
  4598. * [gapUnit](plotOptions.series.gapUnit) option.
  4599. *
  4600. * In case when `dataGrouping` is enabled, points can be grouped
  4601. * into a larger time span. This can make the grouped points to have
  4602. * a greater distance than the absolute value of `gapSize` property,
  4603. * which will result in disappearing graph completely. To prevent
  4604. * this situation the mentioned distance between grouped points is
  4605. * used instead of previously defined `gapSize`.
  4606. *
  4607. * In practice, this option is most often used to visualize gaps in
  4608. * time series. In a stock chart, intraday data is available for
  4609. * daytime hours, while gaps will appear in nights and weekends.
  4610. *
  4611. * @see [gapUnit](plotOptions.series.gapUnit)
  4612. * @see [xAxis.breaks](#xAxis.breaks)
  4613. *
  4614. * @sample {highstock} stock/plotoptions/series-gapsize/
  4615. * Setting the gap size to 2 introduces gaps for weekends
  4616. * in daily datasets.
  4617. *
  4618. * @type {number}
  4619. * @default 0
  4620. * @product highstock
  4621. * @requires modules/broken-axis
  4622. * @apioption plotOptions.series.gapSize
  4623. */
  4624. /**
  4625. * Together with [gapSize](plotOptions.series.gapSize), this option
  4626. * defines where to draw gaps in the graph.
  4627. *
  4628. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  4629. * means that if the distance between two points is greater than
  4630. * 5 times that of the two closest points, the graph will be broken.
  4631. *
  4632. * When the `gapUnit` is `"value"`, the gap is based on absolute
  4633. * axis values, which on a datetime axis is milliseconds. This also
  4634. * applies to the navigator series that inherits gap options from
  4635. * the base series.
  4636. *
  4637. * @see [gapSize](plotOptions.series.gapSize)
  4638. *
  4639. * @type {string}
  4640. * @default relative
  4641. * @since 5.0.13
  4642. * @product highstock
  4643. * @validvalue ["relative", "value"]
  4644. * @requires modules/broken-axis
  4645. * @apioption plotOptions.series.gapUnit
  4646. */
  4647. if (gapSize && i > 0) { // #5008
  4648. // Gap unit is relative
  4649. if (this.options.gapUnit !== 'value') {
  4650. gapSize *= this.basePointRange;
  4651. }
  4652. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  4653. if (groupingSize &&
  4654. groupingSize > gapSize &&
  4655. // Except when DG is forced (e.g. from other series)
  4656. // and has lower granularity than actual points (#11351)
  4657. groupingSize >= this.basePointRange) {
  4658. gapSize = groupingSize;
  4659. }
  4660. // extension for ordinal breaks
  4661. var current = void 0,
  4662. next = void 0;
  4663. while (i--) {
  4664. // Reassign next if it is not visible
  4665. if (!(next && next.visible !== false)) {
  4666. next = points[i + 1];
  4667. }
  4668. current = points[i];
  4669. // Skip iteration if one of the points is not visible
  4670. if (next.visible === false || current.visible === false) {
  4671. continue;
  4672. }
  4673. if (next.x - current.x > gapSize) {
  4674. var xRange = (current.x + next.x) / 2;
  4675. points.splice(// insert after this one
  4676. i + 1, 0, {
  4677. isNull: true,
  4678. x: xRange
  4679. });
  4680. // For stacked chart generate empty stack items, #6546
  4681. if (yAxis.stacking && this.options.stacking) {
  4682. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  4683. new StackItem(yAxis, yAxis.options
  4684. .stackLabels, false, xRange, this.stack);
  4685. stack.total = 0;
  4686. }
  4687. }
  4688. // Assign current to next for the upcoming iteration
  4689. next = current;
  4690. }
  4691. }
  4692. // Call base method
  4693. return this.getGraphPath(points);
  4694. };
  4695. /* eslint-disable no-invalid-this */
  4696. addEvent(AxisClass, 'init', function () {
  4697. var axis = this;
  4698. if (!axis.brokenAxis) {
  4699. axis.brokenAxis = new BrokenAxisAdditions(axis);
  4700. }
  4701. });
  4702. addEvent(AxisClass, 'afterInit', function () {
  4703. if (typeof this.brokenAxis !== 'undefined') {
  4704. this.brokenAxis.setBreaks(this.options.breaks, false);
  4705. }
  4706. });
  4707. addEvent(AxisClass, 'afterSetTickPositions', function () {
  4708. var axis = this;
  4709. var brokenAxis = axis.brokenAxis;
  4710. if (brokenAxis &&
  4711. brokenAxis.hasBreaks) {
  4712. var tickPositions = this.tickPositions,
  4713. info = this.tickPositions.info,
  4714. newPositions = [],
  4715. i;
  4716. for (i = 0; i < tickPositions.length; i++) {
  4717. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  4718. newPositions.push(tickPositions[i]);
  4719. }
  4720. }
  4721. this.tickPositions = newPositions;
  4722. this.tickPositions.info = info;
  4723. }
  4724. });
  4725. // Force Axis to be not-ordinal when breaks are defined
  4726. addEvent(AxisClass, 'afterSetOptions', function () {
  4727. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  4728. this.options.ordinal = false;
  4729. }
  4730. });
  4731. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  4732. var _a = this,
  4733. isDirty = _a.isDirty,
  4734. connectNulls = _a.options.connectNulls,
  4735. points = _a.points,
  4736. xAxis = _a.xAxis,
  4737. yAxis = _a.yAxis;
  4738. // Set, or reset visibility of the points. Axis.setBreaks marks the
  4739. // series as isDirty
  4740. if (isDirty) {
  4741. var i = points.length;
  4742. while (i--) {
  4743. var point = points[i];
  4744. // Respect nulls inside the break (#4275)
  4745. var nullGap = point.y === null && connectNulls === false;
  4746. var isPointInBreak = (!nullGap && ((xAxis &&
  4747. xAxis.brokenAxis &&
  4748. xAxis.brokenAxis.isInAnyBreak(point.x,
  4749. true)) || (yAxis &&
  4750. yAxis.brokenAxis &&
  4751. yAxis.brokenAxis.isInAnyBreak(point.y,
  4752. true))));
  4753. // Set point.visible if in any break.
  4754. // If not in break, reset visible to original value.
  4755. point.visible = isPointInBreak ?
  4756. false :
  4757. point.options.visible !== false;
  4758. }
  4759. }
  4760. });
  4761. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  4762. this.drawBreaks(this.xAxis, ['x']);
  4763. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  4764. });
  4765. };
  4766. return BrokenAxis;
  4767. }());
  4768. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  4769. return BrokenAxis;
  4770. });
  4771. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  4772. });
  4773. _registerModule(_modules, 'Extensions/DataGrouping.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Tooltip.js'], _modules['Core/Utilities.js']], function (Axis, DateTimeAxis, H, O, Point, Series, Tooltip, U) {
  4774. /* *
  4775. *
  4776. * (c) 2010-2021 Torstein Honsi
  4777. *
  4778. * License: www.highcharts.com/license
  4779. *
  4780. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4781. *
  4782. * */
  4783. var seriesProto = Series.prototype;
  4784. var addEvent = U.addEvent,
  4785. arrayMax = U.arrayMax,
  4786. arrayMin = U.arrayMin,
  4787. correctFloat = U.correctFloat,
  4788. defined = U.defined,
  4789. error = U.error,
  4790. extend = U.extend,
  4791. format = U.format,
  4792. isNumber = U.isNumber,
  4793. merge = U.merge,
  4794. pick = U.pick;
  4795. /**
  4796. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  4797. */
  4798. /**
  4799. * @interface Highcharts.DataGroupingInfoObject
  4800. */ /**
  4801. * @name Highcharts.DataGroupingInfoObject#length
  4802. * @type {number}
  4803. */ /**
  4804. * @name Highcharts.DataGroupingInfoObject#options
  4805. * @type {Highcharts.SeriesOptionsType|undefined}
  4806. */ /**
  4807. * @name Highcharts.DataGroupingInfoObject#start
  4808. * @type {number}
  4809. */
  4810. ''; // detach doclets above
  4811. /* ************************************************************************** *
  4812. * Start data grouping module *
  4813. * ************************************************************************** */
  4814. /* eslint-disable no-invalid-this, valid-jsdoc */
  4815. /**
  4816. * Define the available approximation types. The data grouping
  4817. * approximations takes an array or numbers as the first parameter. In case
  4818. * of ohlc, four arrays are sent in as four parameters. Each array consists
  4819. * only of numbers. In case null values belong to the group, the property
  4820. * .hasNulls will be set to true on the array.
  4821. *
  4822. * @product highstock
  4823. *
  4824. * @private
  4825. * @name Highcharts.approximations
  4826. * @type {Highcharts.Dictionary<Function>}
  4827. */
  4828. var approximations = H.approximations = {
  4829. sum: function (arr) {
  4830. var len = arr.length,
  4831. ret;
  4832. // 1. it consists of nulls exclusive
  4833. if (!len && arr.hasNulls) {
  4834. ret = null;
  4835. // 2. it has a length and real values
  4836. }
  4837. else if (len) {
  4838. ret = 0;
  4839. while (len--) {
  4840. ret += arr[len];
  4841. }
  4842. }
  4843. // 3. it has zero length, so just return undefined
  4844. // => doNothing()
  4845. return ret;
  4846. },
  4847. average: function (arr) {
  4848. var len = arr.length,
  4849. ret = approximations.sum(arr);
  4850. // If we have a number, return it divided by the length. If not,
  4851. // return null or undefined based on what the sum method finds.
  4852. if (isNumber(ret) && len) {
  4853. ret = correctFloat(ret / len);
  4854. }
  4855. return ret;
  4856. },
  4857. // The same as average, but for series with multiple values, like area
  4858. // ranges.
  4859. averages: function () {
  4860. var ret = [];
  4861. [].forEach.call(arguments, function (arr) {
  4862. ret.push(approximations.average(arr));
  4863. });
  4864. // Return undefined when first elem. is undefined and let
  4865. // sum method handle null (#7377)
  4866. return typeof ret[0] === 'undefined' ? void 0 : ret;
  4867. },
  4868. open: function (arr) {
  4869. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  4870. },
  4871. high: function (arr) {
  4872. return arr.length ?
  4873. arrayMax(arr) :
  4874. (arr.hasNulls ? null : void 0);
  4875. },
  4876. low: function (arr) {
  4877. return arr.length ?
  4878. arrayMin(arr) :
  4879. (arr.hasNulls ? null : void 0);
  4880. },
  4881. close: function (arr) {
  4882. return arr.length ?
  4883. arr[arr.length - 1] :
  4884. (arr.hasNulls ? null : void 0);
  4885. },
  4886. // ohlc and range are special cases where a multidimensional array is
  4887. // input and an array is output
  4888. ohlc: function (open, high, low, close) {
  4889. open = approximations.open(open);
  4890. high = approximations.high(high);
  4891. low = approximations.low(low);
  4892. close = approximations.close(close);
  4893. if (isNumber(open) ||
  4894. isNumber(high) ||
  4895. isNumber(low) ||
  4896. isNumber(close)) {
  4897. return [open, high, low, close];
  4898. }
  4899. // else, return is undefined
  4900. },
  4901. range: function (low, high) {
  4902. low = approximations.low(low);
  4903. high = approximations.high(high);
  4904. if (isNumber(low) || isNumber(high)) {
  4905. return [low, high];
  4906. }
  4907. if (low === null && high === null) {
  4908. return null;
  4909. }
  4910. // else, return is undefined
  4911. }
  4912. };
  4913. var groupData = function (xData,
  4914. yData,
  4915. groupPositions,
  4916. approximation) {
  4917. var series = this,
  4918. data = series.data,
  4919. dataOptions = series.options && series.options.data,
  4920. groupedXData = [],
  4921. groupedYData = [],
  4922. groupMap = [],
  4923. dataLength = xData.length,
  4924. pointX,
  4925. pointY,
  4926. groupedY,
  4927. // when grouping the fake extended axis for panning,
  4928. // we don't need to consider y
  4929. handleYData = !!yData,
  4930. values = [],
  4931. approximationFn,
  4932. pointArrayMap = series.pointArrayMap,
  4933. pointArrayMapLength = pointArrayMap && pointArrayMap.length,
  4934. extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']),
  4935. pos = 0,
  4936. start = 0,
  4937. valuesLen,
  4938. i,
  4939. j;
  4940. /**
  4941. * @private
  4942. */
  4943. function getApproximation(approx) {
  4944. if (typeof approx === 'function') {
  4945. return approx;
  4946. }
  4947. if (approximations[approx]) {
  4948. return approximations[approx];
  4949. }
  4950. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  4951. 'average'];
  4952. }
  4953. approximationFn = getApproximation(approximation);
  4954. // Calculate values array size from pointArrayMap length
  4955. if (pointArrayMapLength) {
  4956. pointArrayMap.forEach(function () {
  4957. values.push([]);
  4958. });
  4959. }
  4960. else {
  4961. values.push([]);
  4962. }
  4963. valuesLen = pointArrayMapLength || 1;
  4964. // Start with the first point within the X axis range (#2696)
  4965. for (i = 0; i <= dataLength; i++) {
  4966. if (xData[i] >= groupPositions[0]) {
  4967. break;
  4968. }
  4969. }
  4970. for (i; i <= dataLength; i++) {
  4971. // when a new group is entered, summarize and initialize
  4972. // the previous group
  4973. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  4974. xData[i] >= groupPositions[pos + 1]) ||
  4975. i === dataLength) { // get the last group
  4976. // get group x and y
  4977. pointX = groupPositions[pos];
  4978. series.dataGroupInfo = {
  4979. start: series.cropStart + start,
  4980. length: values[0].length
  4981. };
  4982. groupedY = approximationFn.apply(series, values);
  4983. // By default, let options of the first grouped point be passed over
  4984. // to the grouped point. This allows preserving properties like
  4985. // `name` and `color` or custom properties. Implementers can
  4986. // override this from the approximation function, where they can
  4987. // write custom options to `this.dataGroupInfo.options`.
  4988. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  4989. // Convert numbers and arrays into objects
  4990. series.dataGroupInfo.options = merge(series.pointClass.prototype
  4991. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  4992. // Make sure the raw data (x, y, open, high etc) is not copied
  4993. // over and overwriting approximated data.
  4994. extendedPointArrayMap.forEach(function (key) {
  4995. delete series.dataGroupInfo.options[key];
  4996. });
  4997. }
  4998. // push the grouped data
  4999. if (typeof groupedY !== 'undefined') {
  5000. groupedXData.push(pointX);
  5001. groupedYData.push(groupedY);
  5002. groupMap.push(series.dataGroupInfo);
  5003. }
  5004. // reset the aggregate arrays
  5005. start = i;
  5006. for (j = 0; j < valuesLen; j++) {
  5007. values[j].length = 0; // faster than values[j] = []
  5008. values[j].hasNulls = false;
  5009. }
  5010. // Advance on the group positions
  5011. pos += 1;
  5012. // don't loop beyond the last group
  5013. if (i === dataLength) {
  5014. break;
  5015. }
  5016. }
  5017. // break out
  5018. if (i === dataLength) {
  5019. break;
  5020. }
  5021. // for each raw data point, push it to an array that contains all values
  5022. // for this specific group
  5023. if (pointArrayMap) {
  5024. var index = series.cropStart + i,
  5025. point = (data && data[index]) ||
  5026. series.pointClass.prototype.applyOptions.apply({
  5027. series: series
  5028. },
  5029. [dataOptions[index]]),
  5030. val;
  5031. for (j = 0; j < pointArrayMapLength; j++) {
  5032. val = point[pointArrayMap[j]];
  5033. if (isNumber(val)) {
  5034. values[j].push(val);
  5035. }
  5036. else if (val === null) {
  5037. values[j].hasNulls = true;
  5038. }
  5039. }
  5040. }
  5041. else {
  5042. pointY = handleYData ? yData[i] : null;
  5043. if (isNumber(pointY)) {
  5044. values[0].push(pointY);
  5045. }
  5046. else if (pointY === null) {
  5047. values[0].hasNulls = true;
  5048. }
  5049. }
  5050. }
  5051. return {
  5052. groupedXData: groupedXData,
  5053. groupedYData: groupedYData,
  5054. groupMap: groupMap
  5055. };
  5056. };
  5057. var dataGrouping = {
  5058. approximations: approximations,
  5059. groupData: groupData
  5060. };
  5061. // -----------------------------------------------------------------------------
  5062. // The following code applies to implementation of data grouping on a Series
  5063. var baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  5064. /** @ignore */
  5065. commonOptions = {
  5066. // enabled: null, // (true for stock charts, false for basic),
  5067. // forced: undefined,
  5068. groupPixelWidth: 2,
  5069. // the first one is the point or start value, the second is the start
  5070. // value if we're dealing with range, the third one is the end value if
  5071. // dealing with a range
  5072. dateTimeLabelFormats: {
  5073. millisecond: [
  5074. '%A, %b %e, %H:%M:%S.%L',
  5075. '%A, %b %e, %H:%M:%S.%L',
  5076. '-%H:%M:%S.%L'
  5077. ],
  5078. second: [
  5079. '%A, %b %e, %H:%M:%S',
  5080. '%A, %b %e, %H:%M:%S',
  5081. '-%H:%M:%S'
  5082. ],
  5083. minute: [
  5084. '%A, %b %e, %H:%M',
  5085. '%A, %b %e, %H:%M',
  5086. '-%H:%M'
  5087. ],
  5088. hour: [
  5089. '%A, %b %e, %H:%M',
  5090. '%A, %b %e, %H:%M',
  5091. '-%H:%M'
  5092. ],
  5093. day: [
  5094. '%A, %b %e, %Y',
  5095. '%A, %b %e',
  5096. '-%A, %b %e, %Y'
  5097. ],
  5098. week: [
  5099. 'Week from %A, %b %e, %Y',
  5100. '%A, %b %e',
  5101. '-%A, %b %e, %Y'
  5102. ],
  5103. month: [
  5104. '%B %Y',
  5105. '%B',
  5106. '-%B %Y'
  5107. ],
  5108. year: [
  5109. '%Y',
  5110. '%Y',
  5111. '-%Y'
  5112. ]
  5113. }
  5114. // smoothed = false, // enable this for navigator series only
  5115. }, specificOptions = {
  5116. line: {},
  5117. spline: {},
  5118. area: {},
  5119. areaspline: {},
  5120. arearange: {},
  5121. column: {
  5122. groupPixelWidth: 10
  5123. },
  5124. columnrange: {
  5125. groupPixelWidth: 10
  5126. },
  5127. candlestick: {
  5128. groupPixelWidth: 10
  5129. },
  5130. ohlc: {
  5131. groupPixelWidth: 5
  5132. }
  5133. },
  5134. // units are defined in a separate array to allow complete overriding in
  5135. // case of a user option
  5136. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  5137. [
  5138. 'millisecond',
  5139. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5140. ], [
  5141. 'second',
  5142. [1, 2, 5, 10, 15, 30]
  5143. ], [
  5144. 'minute',
  5145. [1, 2, 5, 10, 15, 30]
  5146. ], [
  5147. 'hour',
  5148. [1, 2, 3, 4, 6, 8, 12]
  5149. ], [
  5150. 'day',
  5151. [1]
  5152. ], [
  5153. 'week',
  5154. [1]
  5155. ], [
  5156. 'month',
  5157. [1, 3, 6]
  5158. ], [
  5159. 'year',
  5160. null
  5161. ]
  5162. ];
  5163. // Set default approximations to the prototypes if present. Properties are
  5164. // inherited down. Can be overridden for individual series types.
  5165. seriesProto.getDGApproximation = function () {
  5166. if (this.is('arearange')) {
  5167. return 'range';
  5168. }
  5169. if (this.is('ohlc')) {
  5170. return 'ohlc';
  5171. }
  5172. if (this.is('column')) {
  5173. return 'sum';
  5174. }
  5175. return 'average';
  5176. };
  5177. /**
  5178. * Takes parallel arrays of x and y data and groups the data into intervals
  5179. * defined by groupPositions, a collection of starting x values for each group.
  5180. *
  5181. * @private
  5182. * @function Highcharts.Series#groupData
  5183. *
  5184. * @param {Array<number>} xData
  5185. *
  5186. * @param {Array<number>|Array<Array<number>>} yData
  5187. *
  5188. * @param {boolean} groupPositions
  5189. *
  5190. * @param {string|Function} approximation
  5191. *
  5192. * @return {void}
  5193. */
  5194. seriesProto.groupData = groupData;
  5195. // Extend the basic processData method, that crops the data to the current zoom
  5196. // range, with data grouping logic.
  5197. seriesProto.processData = function () {
  5198. var series = this,
  5199. chart = series.chart,
  5200. options = series.options,
  5201. dataGroupingOptions = options.dataGrouping,
  5202. groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  5203. pick(dataGroupingOptions.enabled,
  5204. chart.options.isStock),
  5205. visible = (series.visible || !chart.options.chart.ignoreHiddenSeries),
  5206. hasGroupedData,
  5207. skip,
  5208. lastDataGrouping = this.currentDataGrouping,
  5209. currentDataGrouping,
  5210. croppedData,
  5211. revertRequireSorting = false;
  5212. // Run base method
  5213. series.forceCrop = groupingEnabled; // #334
  5214. series.groupPixelWidth = null; // #2110
  5215. series.hasProcessed = true; // #2692
  5216. // Data needs to be sorted for dataGrouping
  5217. if (groupingEnabled && !series.requireSorting) {
  5218. series.requireSorting = revertRequireSorting = true;
  5219. }
  5220. // Skip if processData returns false or if grouping is disabled (in that
  5221. // order)
  5222. skip = (baseProcessData.apply(series, arguments) === false ||
  5223. !groupingEnabled);
  5224. // Revert original requireSorting value if changed
  5225. if (revertRequireSorting) {
  5226. series.requireSorting = false;
  5227. }
  5228. if (!skip) {
  5229. series.destroyGroupedData();
  5230. var i,
  5231. processedXData = dataGroupingOptions.groupAll ?
  5232. series.xData :
  5233. series.processedXData,
  5234. processedYData = dataGroupingOptions.groupAll ?
  5235. series.yData :
  5236. series.processedYData,
  5237. plotSizeX = chart.plotSizeX,
  5238. xAxis = series.xAxis,
  5239. ordinal = xAxis.options.ordinal,
  5240. groupPixelWidth = series.groupPixelWidth =
  5241. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  5242. // Execute grouping if the amount of points is greater than the limit
  5243. // defined in groupPixelWidth
  5244. if (groupPixelWidth) {
  5245. hasGroupedData = true;
  5246. // Force recreation of point instances in series.translate, #5699
  5247. series.isDirty = true;
  5248. series.points = null; // #6709
  5249. var extremes = xAxis.getExtremes(),
  5250. xMin = extremes.min,
  5251. xMax = extremes.max,
  5252. groupIntervalFactor = (ordinal &&
  5253. xAxis.ordinal &&
  5254. xAxis.ordinal.getGroupIntervalFactor(xMin,
  5255. xMax,
  5256. series)) || 1,
  5257. interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  5258. groupIntervalFactor,
  5259. groupPositions = xAxis.getTimeTicks(DateTimeAxis.AdditionsClass.prototype.normalizeTimeTickInterval(interval,
  5260. dataGroupingOptions.units ||
  5261. defaultDataGroupingUnits),
  5262. // Processed data may extend beyond axis (#4907)
  5263. Math.min(xMin,
  5264. processedXData[0]),
  5265. Math.max(xMax,
  5266. processedXData[processedXData.length - 1]),
  5267. xAxis.options.startOfWeek,
  5268. processedXData,
  5269. series.closestPointRange),
  5270. groupedData = seriesProto.groupData.apply(series,
  5271. [
  5272. processedXData,
  5273. processedYData,
  5274. groupPositions,
  5275. dataGroupingOptions.approximation
  5276. ]),
  5277. groupedXData = groupedData.groupedXData,
  5278. groupedYData = groupedData.groupedYData,
  5279. gapSize = 0;
  5280. // Prevent the smoothed data to spill out left and right, and make
  5281. // sure data is not shifted to the left
  5282. if (dataGroupingOptions.smoothed && groupedXData.length) {
  5283. i = groupedXData.length - 1;
  5284. groupedXData[i] = Math.min(groupedXData[i], xMax);
  5285. while (i-- && i > 0) {
  5286. groupedXData[i] += interval / 2;
  5287. }
  5288. groupedXData[0] = Math.max(groupedXData[0], xMin);
  5289. }
  5290. // Record what data grouping values were used
  5291. for (i = 1; i < groupPositions.length; i++) {
  5292. // The grouped gapSize needs to be the largest distance between
  5293. // the group to capture varying group sizes like months or DST
  5294. // crossing (#10000). Also check that the gap is not at the
  5295. // start of a segment.
  5296. if (!groupPositions.info.segmentStarts ||
  5297. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  5298. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  5299. }
  5300. }
  5301. currentDataGrouping = groupPositions.info;
  5302. currentDataGrouping.gapSize = gapSize;
  5303. series.closestPointRange = groupPositions.info.totalRange;
  5304. series.groupMap = groupedData.groupMap;
  5305. // Make sure the X axis extends to show the first group (#2533)
  5306. // But only for visible series (#5493, #6393)
  5307. if (defined(groupedXData[0]) &&
  5308. groupedXData[0] < xAxis.min &&
  5309. visible) {
  5310. if ((!defined(xAxis.options.min) &&
  5311. xAxis.min <= xAxis.dataMin) ||
  5312. xAxis.min === xAxis.dataMin) {
  5313. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  5314. }
  5315. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  5316. }
  5317. // We calculated all group positions but we should render
  5318. // only the ones within the visible range
  5319. if (dataGroupingOptions.groupAll) {
  5320. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  5321. );
  5322. groupedXData = croppedData.xData;
  5323. groupedYData = croppedData.yData;
  5324. }
  5325. // Set series props
  5326. series.processedXData = groupedXData;
  5327. series.processedYData = groupedYData;
  5328. }
  5329. else {
  5330. series.groupMap = null;
  5331. }
  5332. series.hasGroupedData = hasGroupedData;
  5333. series.currentDataGrouping = currentDataGrouping;
  5334. series.preventGraphAnimation =
  5335. (lastDataGrouping && lastDataGrouping.totalRange) !==
  5336. (currentDataGrouping && currentDataGrouping.totalRange);
  5337. }
  5338. };
  5339. // Destroy the grouped data points. #622, #740
  5340. seriesProto.destroyGroupedData = function () {
  5341. // Clear previous groups
  5342. if (this.groupedData) {
  5343. this.groupedData.forEach(function (point, i) {
  5344. if (point) {
  5345. this.groupedData[i] = point.destroy ?
  5346. point.destroy() : null;
  5347. }
  5348. }, this);
  5349. // Clears all:
  5350. // - `this.groupedData`
  5351. // - `this.points`
  5352. // - `preserve` object in series.update()
  5353. this.groupedData.length = 0;
  5354. }
  5355. };
  5356. // Override the generatePoints method by adding a reference to grouped data
  5357. seriesProto.generatePoints = function () {
  5358. baseGeneratePoints.apply(this);
  5359. // Record grouped data in order to let it be destroyed the next time
  5360. // processData runs
  5361. this.destroyGroupedData(); // #622
  5362. this.groupedData = this.hasGroupedData ? this.points : null;
  5363. };
  5364. // Override point prototype to throw a warning when trying to update grouped
  5365. // points.
  5366. addEvent(Point, 'update', function () {
  5367. if (this.dataGroup) {
  5368. error(24, false, this.series.chart);
  5369. return false;
  5370. }
  5371. });
  5372. // Extend the original method, make the tooltip's header reflect the grouped
  5373. // range.
  5374. addEvent(Tooltip, 'headerFormatter', function (e) {
  5375. var tooltip = this,
  5376. chart = this.chart,
  5377. time = chart.time,
  5378. labelConfig = e.labelConfig,
  5379. series = labelConfig.series,
  5380. options = series.options,
  5381. tooltipOptions = series.tooltipOptions,
  5382. dataGroupingOptions = options.dataGrouping,
  5383. xDateFormat = tooltipOptions.xDateFormat,
  5384. xDateFormatEnd,
  5385. xAxis = series.xAxis,
  5386. currentDataGrouping,
  5387. dateTimeLabelFormats,
  5388. labelFormats,
  5389. formattedKey,
  5390. formatString = tooltipOptions[(e.isFooter ? 'footer' : 'header') + 'Format'];
  5391. // apply only to grouped series
  5392. if (xAxis &&
  5393. xAxis.options.type === 'datetime' &&
  5394. dataGroupingOptions &&
  5395. isNumber(labelConfig.key)) {
  5396. // set variables
  5397. currentDataGrouping = series.currentDataGrouping;
  5398. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  5399. // Fallback to commonOptions (#9693)
  5400. commonOptions.dateTimeLabelFormats;
  5401. // if we have grouped data, use the grouping information to get the
  5402. // right format
  5403. if (currentDataGrouping) {
  5404. labelFormats =
  5405. dateTimeLabelFormats[currentDataGrouping.unitName];
  5406. if (currentDataGrouping.count === 1) {
  5407. xDateFormat = labelFormats[0];
  5408. }
  5409. else {
  5410. xDateFormat = labelFormats[1];
  5411. xDateFormatEnd = labelFormats[2];
  5412. }
  5413. // if not grouped, and we don't have set the xDateFormat option, get the
  5414. // best fit, so if the least distance between points is one minute, show
  5415. // it, but if the least distance is one day, skip hours and minutes etc.
  5416. }
  5417. else if (!xDateFormat && dateTimeLabelFormats) {
  5418. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  5419. }
  5420. // now format the key
  5421. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  5422. if (xDateFormatEnd) {
  5423. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  5424. }
  5425. // Replace default header style with class name
  5426. if (series.chart.styledMode) {
  5427. formatString = this.styledModeFormat(formatString);
  5428. }
  5429. // return the replaced format
  5430. e.text = format(formatString, {
  5431. point: extend(labelConfig.point, { key: formattedKey }),
  5432. series: series
  5433. }, chart);
  5434. e.preventDefault();
  5435. }
  5436. });
  5437. // Destroy grouped data on series destroy
  5438. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  5439. // Handle default options for data grouping. This must be set at runtime because
  5440. // some series types are defined after this.
  5441. addEvent(Series, 'afterSetOptions', function (e) {
  5442. var options = e.options,
  5443. type = this.type,
  5444. plotOptions = this.chart.options.plotOptions,
  5445. defaultOptions = O.defaultOptions.plotOptions[type].dataGrouping,
  5446. // External series, for example technical indicators should also
  5447. // inherit commonOptions which are not available outside this module
  5448. baseOptions = this.useCommonDataGrouping && commonOptions;
  5449. if (specificOptions[type] || baseOptions) { // #1284
  5450. if (!defaultOptions) {
  5451. defaultOptions = merge(commonOptions, specificOptions[type]);
  5452. }
  5453. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  5454. // Set by the StockChart constructor:
  5455. plotOptions[type].dataGrouping, this.userOptions.dataGrouping);
  5456. }
  5457. });
  5458. // When resetting the scale reset the hasProccessed flag to avoid taking
  5459. // previous data grouping of neighbour series into accound when determining
  5460. // group pixel width (#2692).
  5461. addEvent(Axis, 'afterSetScale', function () {
  5462. this.series.forEach(function (series) {
  5463. series.hasProcessed = false;
  5464. });
  5465. });
  5466. // Get the data grouping pixel width based on the greatest defined individual
  5467. // width of the axis' series, and if whether one of the axes need grouping.
  5468. Axis.prototype.getGroupPixelWidth = function () {
  5469. var series = this.series,
  5470. len = series.length,
  5471. i,
  5472. groupPixelWidth = 0,
  5473. doGrouping = false,
  5474. dataLength,
  5475. dgOptions;
  5476. // If multiple series are compared on the same x axis, give them the same
  5477. // group pixel width (#334)
  5478. i = len;
  5479. while (i--) {
  5480. dgOptions = series[i].options.dataGrouping;
  5481. if (dgOptions) {
  5482. groupPixelWidth = Math.max(groupPixelWidth,
  5483. // Fallback to commonOptions (#9693)
  5484. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  5485. }
  5486. }
  5487. // If one of the series needs grouping, apply it to all (#1634)
  5488. i = len;
  5489. while (i--) {
  5490. dgOptions = series[i].options.dataGrouping;
  5491. if (dgOptions && series[i].hasProcessed) { // #2692
  5492. dataLength = (series[i].processedXData || series[i].data).length;
  5493. // Execute grouping if the amount of points is greater than the
  5494. // limit defined in groupPixelWidth
  5495. if (series[i].groupPixelWidth ||
  5496. dataLength >
  5497. (this.chart.plotSizeX / groupPixelWidth) ||
  5498. (dataLength && dgOptions.forced)) {
  5499. doGrouping = true;
  5500. }
  5501. }
  5502. }
  5503. return doGrouping ? groupPixelWidth : 0;
  5504. };
  5505. /**
  5506. * Highstock only. Force data grouping on all the axis' series.
  5507. *
  5508. * @product highstock
  5509. *
  5510. * @function Highcharts.Axis#setDataGrouping
  5511. *
  5512. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  5513. * A `dataGrouping` configuration. Use `false` to disable data grouping
  5514. * dynamically.
  5515. *
  5516. * @param {boolean} [redraw=true]
  5517. * Whether to redraw the chart or wait for a later call to
  5518. * {@link Chart#redraw}.
  5519. *
  5520. * @return {void}
  5521. */
  5522. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  5523. var axis = this;
  5524. var i;
  5525. redraw = pick(redraw, true);
  5526. if (!dataGrouping) {
  5527. dataGrouping = {
  5528. forced: false,
  5529. units: null
  5530. };
  5531. }
  5532. // Axis is instantiated, update all series
  5533. if (this instanceof Axis) {
  5534. i = this.series.length;
  5535. while (i--) {
  5536. this.series[i].update({
  5537. dataGrouping: dataGrouping
  5538. }, false);
  5539. }
  5540. // Axis not yet instanciated, alter series options
  5541. }
  5542. else {
  5543. this.chart.options.series.forEach(function (seriesOptions) {
  5544. seriesOptions.dataGrouping = dataGrouping;
  5545. }, false);
  5546. }
  5547. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  5548. if (axis.ordinal) {
  5549. axis.ordinal.slope = void 0;
  5550. }
  5551. if (redraw) {
  5552. this.chart.redraw();
  5553. }
  5554. };
  5555. H.dataGrouping = dataGrouping;
  5556. /* eslint-enable no-invalid-this, valid-jsdoc */
  5557. /**
  5558. * Data grouping is the concept of sampling the data values into larger
  5559. * blocks in order to ease readability and increase performance of the
  5560. * JavaScript charts. Highstock by default applies data grouping when
  5561. * the points become closer than a certain pixel value, determined by
  5562. * the `groupPixelWidth` option.
  5563. *
  5564. * If data grouping is applied, the grouping information of grouped
  5565. * points can be read from the [Point.dataGroup](
  5566. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  5567. * the data itself are set, for example `name` or `color` or custom properties,
  5568. * the grouping logic doesn't know how to group it. In this case the options of
  5569. * the first point instance are copied over to the group point. This can be
  5570. * altered through a custom `approximation` callback function.
  5571. *
  5572. * @declare Highcharts.DataGroupingOptionsObject
  5573. * @product highstock
  5574. * @requires product:highstock
  5575. * @requires module:modules/datagrouping
  5576. * @apioption plotOptions.series.dataGrouping
  5577. */
  5578. /**
  5579. * The method of approximation inside a group. When for example 30 days
  5580. * are grouped into one month, this determines what value should represent
  5581. * the group. Possible values are "average", "averages", "open", "high",
  5582. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  5583. * is "ohlc" by default, which finds the open, high, low and close values
  5584. * within all the grouped data. For ranges, the approximation is "range",
  5585. * which finds the low and high values. For multi-dimensional data,
  5586. * like ranges and OHLC, "averages" will compute the average for each
  5587. * dimension.
  5588. *
  5589. * Custom aggregate methods can be added by assigning a callback function
  5590. * as the approximation. This function takes a numeric array as the
  5591. * argument and should return a single numeric value or `null`. Note
  5592. * that the numeric array will never contain null values, only true
  5593. * numbers. Instead, if null values are present in the raw data, the
  5594. * numeric array will have an `.hasNulls` property set to `true`. For
  5595. * single-value data sets the data is available in the first argument
  5596. * of the callback function. For OHLC data sets, all the open values
  5597. * are in the first argument, all high values in the second etc.
  5598. *
  5599. * Since v4.2.7, grouping meta data is available in the approximation
  5600. * callback from `this.dataGroupInfo`. It can be used to extract information
  5601. * from the raw data.
  5602. *
  5603. * Defaults to `average` for line-type series, `sum` for columns, `range`
  5604. * for range series and `ohlc` for OHLC and candlestick.
  5605. *
  5606. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  5607. * Approximation callback with custom data
  5608. *
  5609. * @type {Highcharts.DataGroupingApproximationValue|Function}
  5610. * @apioption plotOptions.series.dataGrouping.approximation
  5611. */
  5612. /**
  5613. * Datetime formats for the header of the tooltip in a stock chart.
  5614. * The format can vary within a chart depending on the currently selected
  5615. * time range and the current data grouping.
  5616. *
  5617. * The default formats are:
  5618. * ```js
  5619. * {
  5620. * millisecond: [
  5621. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  5622. * ],
  5623. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  5624. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5625. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5626. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5627. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5628. * month: ['%B %Y', '%B', '-%B %Y'],
  5629. * year: ['%Y', '%Y', '-%Y']
  5630. * }
  5631. * ```
  5632. *
  5633. * For each of these array definitions, the first item is the format
  5634. * used when the active time span is one unit. For instance, if the
  5635. * current data applies to one week, the first item of the week array
  5636. * is used. The second and third items are used when the active time
  5637. * span is more than two units. For instance, if the current data applies
  5638. * to two weeks, the second and third item of the week array are used,
  5639. * and applied to the start and end date of the time span.
  5640. *
  5641. * @type {object}
  5642. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  5643. */
  5644. /**
  5645. * Enable or disable data grouping.
  5646. *
  5647. * @type {boolean}
  5648. * @default true
  5649. * @apioption plotOptions.series.dataGrouping.enabled
  5650. */
  5651. /**
  5652. * When data grouping is forced, it runs no matter how small the intervals
  5653. * are. This can be handy for example when the sum should be calculated
  5654. * for values appearing at random times within each hour.
  5655. *
  5656. * @type {boolean}
  5657. * @default false
  5658. * @apioption plotOptions.series.dataGrouping.forced
  5659. */
  5660. /**
  5661. * The approximate pixel width of each group. If for example a series
  5662. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5663. * is performed. If however the series contains so many points that
  5664. * the spacing is less than the groupPixelWidth, Highcharts will try
  5665. * to group it into appropriate groups so that each is more or less
  5666. * two pixels wide. If multiple series with different group pixel widths
  5667. * are drawn on the same x axis, all series will take the greatest width.
  5668. * For example, line series have 2px default group width, while column
  5669. * series have 10px. If combined, both the line and the column will
  5670. * have 10px by default.
  5671. *
  5672. * @type {number}
  5673. * @default 2
  5674. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  5675. */
  5676. /**
  5677. * By default only points within the visible range are grouped. Enabling this
  5678. * option will force data grouping to calculate all grouped points for a given
  5679. * dataset. That option prevents for example a column series from calculating
  5680. * a grouped point partially. The effect is similar to
  5681. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  5682. * not affect yAxis extremes.
  5683. *
  5684. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  5685. * Two series with the same data but different groupAll setting
  5686. *
  5687. * @type {boolean}
  5688. * @default false
  5689. * @since 6.1.0
  5690. * @apioption plotOptions.series.dataGrouping.groupAll
  5691. */
  5692. /**
  5693. * Normally, a group is indexed by the start of that group, so for example
  5694. * when 30 daily values are grouped into one month, that month's x value
  5695. * will be the 1st of the month. This apparently shifts the data to
  5696. * the left. When the smoothed option is true, this is compensated for.
  5697. * The data is shifted to the middle of the group, and min and max
  5698. * values are preserved. Internally, this is used in the Navigator series.
  5699. *
  5700. * @type {boolean}
  5701. * @default false
  5702. * @apioption plotOptions.series.dataGrouping.smoothed
  5703. */
  5704. /**
  5705. * An array determining what time intervals the data is allowed to be
  5706. * grouped to. Each array item is an array where the first value is
  5707. * the time unit and the second value another array of allowed multiples.
  5708. *
  5709. * Defaults to:
  5710. * ```js
  5711. * units: [[
  5712. * 'millisecond', // unit name
  5713. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5714. * ], [
  5715. * 'second',
  5716. * [1, 2, 5, 10, 15, 30]
  5717. * ], [
  5718. * 'minute',
  5719. * [1, 2, 5, 10, 15, 30]
  5720. * ], [
  5721. * 'hour',
  5722. * [1, 2, 3, 4, 6, 8, 12]
  5723. * ], [
  5724. * 'day',
  5725. * [1]
  5726. * ], [
  5727. * 'week',
  5728. * [1]
  5729. * ], [
  5730. * 'month',
  5731. * [1, 3, 6]
  5732. * ], [
  5733. * 'year',
  5734. * null
  5735. * ]]
  5736. * ```
  5737. *
  5738. * @type {Array<Array<string,(Array<number>|null)>>}
  5739. * @apioption plotOptions.series.dataGrouping.units
  5740. */
  5741. /**
  5742. * The approximate pixel width of each group. If for example a series
  5743. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5744. * is performed. If however the series contains so many points that
  5745. * the spacing is less than the groupPixelWidth, Highcharts will try
  5746. * to group it into appropriate groups so that each is more or less
  5747. * two pixels wide. Defaults to `10`.
  5748. *
  5749. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  5750. * Two series with the same data density but different groupPixelWidth
  5751. *
  5752. * @type {number}
  5753. * @default 10
  5754. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  5755. */
  5756. ''; // required by JSDoc parsing
  5757. return dataGrouping;
  5758. });
  5759. _registerModule(_modules, 'Series/OHLC/OHLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  5760. /* *
  5761. *
  5762. * (c) 2010-2021 Torstein Honsi
  5763. *
  5764. * License: www.highcharts.com/license
  5765. *
  5766. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5767. *
  5768. * */
  5769. var __extends = (this && this.__extends) || (function () {
  5770. var extendStatics = function (d,
  5771. b) {
  5772. extendStatics = Object.setPrototypeOf ||
  5773. ({ __proto__: [] } instanceof Array && function (d,
  5774. b) { d.__proto__ = b; }) ||
  5775. function (d,
  5776. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5777. return extendStatics(d, b);
  5778. };
  5779. return function (d, b) {
  5780. extendStatics(d, b);
  5781. function __() { this.constructor = d; }
  5782. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5783. };
  5784. })();
  5785. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  5786. /* *
  5787. *
  5788. * Class
  5789. *
  5790. * */
  5791. var OHLCPoint = /** @class */ (function (_super) {
  5792. __extends(OHLCPoint, _super);
  5793. function OHLCPoint() {
  5794. /* *
  5795. *
  5796. * Properties
  5797. *
  5798. * */
  5799. var _this = _super !== null && _super.apply(this,
  5800. arguments) || this;
  5801. _this.close = void 0;
  5802. _this.high = void 0;
  5803. _this.low = void 0;
  5804. _this.open = void 0;
  5805. _this.options = void 0;
  5806. _this.plotClose = void 0;
  5807. _this.plotOpen = void 0;
  5808. _this.series = void 0;
  5809. return _this;
  5810. /* eslint-enable valid-jsdoc */
  5811. }
  5812. /* *
  5813. *
  5814. * Functions
  5815. *
  5816. * */
  5817. /* eslint-disable valid-jsdoc */
  5818. /**
  5819. * Extend the parent method by adding up or down to the class name.
  5820. * @private
  5821. * @function Highcharts.seriesTypes.ohlc#getClassName
  5822. * @return {string}
  5823. */
  5824. OHLCPoint.prototype.getClassName = function () {
  5825. return _super.prototype.getClassName.call(this) +
  5826. (this.open < this.close ?
  5827. ' highcharts-point-up' :
  5828. ' highcharts-point-down');
  5829. };
  5830. /**
  5831. * Save upColor as point color (#14826).
  5832. * @private
  5833. * @function Highcharts.seriesTypes.ohlc#resolveUpColor
  5834. */
  5835. OHLCPoint.prototype.resolveUpColor = function () {
  5836. if (this.open < this.close &&
  5837. !this.options.color &&
  5838. this.series.options.upColor) {
  5839. this.color = this.series.options.upColor;
  5840. }
  5841. };
  5842. /**
  5843. * Extend the parent method by saving upColor.
  5844. * @private
  5845. * @function Highcharts.seriesTypes.ohlc#resolveColor
  5846. */
  5847. OHLCPoint.prototype.resolveColor = function () {
  5848. _super.prototype.resolveColor.call(this);
  5849. this.resolveUpColor();
  5850. };
  5851. /**
  5852. * Extend the parent method by saving upColor.
  5853. * @private
  5854. * @function Highcharts.seriesTypes.ohlc#getZone
  5855. *
  5856. * @return {Highcharts.SeriesZonesOptionsObject}
  5857. * The zone item.
  5858. */
  5859. OHLCPoint.prototype.getZone = function () {
  5860. var zone = _super.prototype.getZone.call(this);
  5861. this.resolveUpColor();
  5862. return zone;
  5863. };
  5864. return OHLCPoint;
  5865. }(ColumnSeries.prototype.pointClass));
  5866. /* *
  5867. *
  5868. * Default Export
  5869. *
  5870. * */
  5871. return OHLCPoint;
  5872. });
  5873. _registerModule(_modules, 'Series/OHLC/OHLCSeries.js', [_modules['Series/OHLC/OHLCPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (OHLCPoint, SeriesRegistry, U) {
  5874. /* *
  5875. *
  5876. * (c) 2010-2021 Torstein Honsi
  5877. *
  5878. * License: www.highcharts.com/license
  5879. *
  5880. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5881. *
  5882. * */
  5883. var __extends = (this && this.__extends) || (function () {
  5884. var extendStatics = function (d,
  5885. b) {
  5886. extendStatics = Object.setPrototypeOf ||
  5887. ({ __proto__: [] } instanceof Array && function (d,
  5888. b) { d.__proto__ = b; }) ||
  5889. function (d,
  5890. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5891. return extendStatics(d, b);
  5892. };
  5893. return function (d, b) {
  5894. extendStatics(d, b);
  5895. function __() { this.constructor = d; }
  5896. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5897. };
  5898. })();
  5899. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  5900. var extend = U.extend,
  5901. merge = U.merge;
  5902. /* *
  5903. *
  5904. * Class
  5905. *
  5906. * */
  5907. /**
  5908. * The ohlc series type.
  5909. *
  5910. * @private
  5911. * @class
  5912. * @name Highcharts.seriesTypes.ohlc
  5913. *
  5914. * @augments Highcharts.Series
  5915. */
  5916. var OHLCSeries = /** @class */ (function (_super) {
  5917. __extends(OHLCSeries, _super);
  5918. function OHLCSeries() {
  5919. /* *
  5920. *
  5921. * Static Properties
  5922. *
  5923. * */
  5924. var _this = _super !== null && _super.apply(this,
  5925. arguments) || this;
  5926. /* *
  5927. *
  5928. * Properties
  5929. *
  5930. * */
  5931. _this.data = void 0;
  5932. _this.options = void 0;
  5933. _this.points = void 0;
  5934. _this.yData = void 0;
  5935. return _this;
  5936. /* eslint-enable valid-jsdoc */
  5937. }
  5938. /* *
  5939. *
  5940. * Functions
  5941. *
  5942. * */
  5943. /* eslint-disable valid-jsdoc */
  5944. /**
  5945. * Draw the data points
  5946. * @private
  5947. */
  5948. OHLCSeries.prototype.drawPoints = function () {
  5949. var series = this,
  5950. points = series.points,
  5951. chart = series.chart,
  5952. /**
  5953. * Extend vertical stem to open and close values.
  5954. */
  5955. extendStem = function (path,
  5956. halfStrokeWidth,
  5957. openOrClose) {
  5958. var start = path[0];
  5959. var end = path[1];
  5960. // We don't need to worry about crisp - openOrClose value
  5961. // is already crisped and halfStrokeWidth should remove it.
  5962. if (typeof start[2] === 'number') {
  5963. start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
  5964. }
  5965. if (typeof end[2] === 'number') {
  5966. end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
  5967. }
  5968. };
  5969. points.forEach(function (point) {
  5970. var plotOpen,
  5971. plotClose,
  5972. crispCorr,
  5973. halfWidth,
  5974. path,
  5975. graphic = point.graphic,
  5976. crispX,
  5977. isNew = !graphic,
  5978. strokeWidth;
  5979. if (typeof point.plotY !== 'undefined') {
  5980. // Create and/or update the graphic
  5981. if (!graphic) {
  5982. point.graphic = graphic = chart.renderer.path()
  5983. .add(series.group);
  5984. }
  5985. if (!chart.styledMode) {
  5986. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  5987. }
  5988. // crisp vector coordinates
  5989. strokeWidth = graphic.strokeWidth();
  5990. crispCorr = (strokeWidth % 2) / 2;
  5991. // #2596:
  5992. crispX = Math.round(point.plotX) - crispCorr;
  5993. halfWidth = Math.round(point.shapeArgs.width / 2);
  5994. // the vertical stem
  5995. path = [
  5996. ['M', crispX, Math.round(point.yBottom)],
  5997. ['L', crispX, Math.round(point.plotHigh)]
  5998. ];
  5999. // open
  6000. if (point.open !== null) {
  6001. plotOpen = Math.round(point.plotOpen) + crispCorr;
  6002. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  6003. extendStem(path, strokeWidth / 2, plotOpen);
  6004. }
  6005. // close
  6006. if (point.close !== null) {
  6007. plotClose = Math.round(point.plotClose) + crispCorr;
  6008. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  6009. extendStem(path, strokeWidth / 2, plotClose);
  6010. }
  6011. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6012. .addClass(point.getClassName(), true);
  6013. }
  6014. });
  6015. };
  6016. /**
  6017. * @private
  6018. * @function Highcarts.seriesTypes.ohlc#init
  6019. * @return {void}
  6020. */
  6021. OHLCSeries.prototype.init = function () {
  6022. _super.prototype.init.apply(this, arguments);
  6023. this.options.stacking = void 0; // #8817
  6024. };
  6025. /**
  6026. * Postprocess mapping between options and SVG attributes
  6027. * @private
  6028. */
  6029. OHLCSeries.prototype.pointAttribs = function (point, state) {
  6030. var attribs = _super.prototype.pointAttribs.call(this,
  6031. point,
  6032. state),
  6033. options = this.options;
  6034. delete attribs.fill;
  6035. if (!point.options.color &&
  6036. options.upColor &&
  6037. point.open < point.close) {
  6038. attribs.stroke = options.upColor;
  6039. }
  6040. return attribs;
  6041. };
  6042. OHLCSeries.prototype.toYData = function (point) {
  6043. // return a plain array for speedy calculation
  6044. return [point.open, point.high, point.low, point.close];
  6045. };
  6046. /**
  6047. * Translate data points from raw values x and y to plotX and plotY
  6048. *
  6049. * @private
  6050. * @function Highcharts.seriesTypes.ohlc#translate
  6051. * @return {void}
  6052. */
  6053. OHLCSeries.prototype.translate = function () {
  6054. var series = this,
  6055. yAxis = series.yAxis,
  6056. hasModifyValue = !!series.modifyValue,
  6057. translated = [
  6058. 'plotOpen',
  6059. 'plotHigh',
  6060. 'plotLow',
  6061. 'plotClose',
  6062. 'yBottom'
  6063. ]; // translate OHLC for
  6064. _super.prototype.translate.apply(series);
  6065. // Do the translation
  6066. series.points.forEach(function (point) {
  6067. [point.open, point.high, point.low, point.close, point.low]
  6068. .forEach(function (value, i) {
  6069. if (value !== null) {
  6070. if (hasModifyValue) {
  6071. value = series.modifyValue(value);
  6072. }
  6073. point[translated[i]] =
  6074. yAxis.toPixels(value, true);
  6075. }
  6076. });
  6077. // Align the tooltip to the high value to avoid covering the
  6078. // point
  6079. point.tooltipPos[1] =
  6080. point.plotHigh + yAxis.pos - series.chart.plotTop;
  6081. });
  6082. };
  6083. /**
  6084. * An OHLC chart is a style of financial chart used to describe price
  6085. * movements over time. It displays open, high, low and close values per
  6086. * data point.
  6087. *
  6088. * @sample stock/demo/ohlc/
  6089. * OHLC chart
  6090. *
  6091. * @extends plotOptions.column
  6092. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  6093. * stack
  6094. * @product highstock
  6095. * @optionparent plotOptions.ohlc
  6096. */
  6097. OHLCSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  6098. /**
  6099. * The approximate pixel width of each group. If for example a series
  6100. * with 30 points is displayed over a 600 pixel wide plot area, no
  6101. * grouping is performed. If however the series contains so many points
  6102. * that the spacing is less than the groupPixelWidth, Highcharts will
  6103. * try to group it into appropriate groups so that each is more or less
  6104. * two pixels wide. Defaults to `5`.
  6105. *
  6106. * @type {number}
  6107. * @default 5
  6108. * @product highstock
  6109. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  6110. */
  6111. /**
  6112. * The pixel width of the line/border. Defaults to `1`.
  6113. *
  6114. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  6115. * A greater line width
  6116. *
  6117. * @type {number}
  6118. * @default 1
  6119. * @product highstock
  6120. *
  6121. * @private
  6122. */
  6123. lineWidth: 1,
  6124. tooltip: {
  6125. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  6126. '<b> {series.name}</b><br/>' +
  6127. 'Open: {point.open}<br/>' +
  6128. 'High: {point.high}<br/>' +
  6129. 'Low: {point.low}<br/>' +
  6130. 'Close: {point.close}<br/>'
  6131. },
  6132. threshold: null,
  6133. states: {
  6134. /**
  6135. * @extends plotOptions.column.states.hover
  6136. * @product highstock
  6137. */
  6138. hover: {
  6139. /**
  6140. * The pixel width of the line representing the OHLC point.
  6141. *
  6142. * @type {number}
  6143. * @default 3
  6144. * @product highstock
  6145. */
  6146. lineWidth: 3
  6147. }
  6148. },
  6149. /**
  6150. * Determines which one of `open`, `high`, `low`, `close` values should
  6151. * be represented as `point.y`, which is later used to set dataLabel
  6152. * position and [compare](#plotOptions.series.compare).
  6153. *
  6154. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  6155. * Possible values
  6156. *
  6157. * @type {string}
  6158. * @default close
  6159. * @validvalue ["open", "high", "low", "close"]
  6160. * @product highstock
  6161. * @apioption plotOptions.ohlc.pointValKey
  6162. */
  6163. /**
  6164. * @default close
  6165. * @apioption plotOptions.ohlc.colorKey
  6166. */
  6167. /**
  6168. * Line color for up points.
  6169. *
  6170. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6171. * @product highstock
  6172. * @apioption plotOptions.ohlc.upColor
  6173. */
  6174. stickyTracking: true
  6175. });
  6176. return OHLCSeries;
  6177. }(ColumnSeries));
  6178. extend(OHLCSeries.prototype, {
  6179. animate: null,
  6180. directTouch: false,
  6181. pointArrayMap: ['open', 'high', 'low', 'close'],
  6182. pointAttrToOptions: {
  6183. stroke: 'color',
  6184. 'stroke-width': 'lineWidth'
  6185. },
  6186. pointValKey: 'close'
  6187. });
  6188. OHLCSeries.prototype.pointClass = OHLCPoint;
  6189. SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
  6190. /* *
  6191. *
  6192. * Default Export
  6193. *
  6194. * */
  6195. /* *
  6196. *
  6197. * API Options
  6198. *
  6199. * */
  6200. /**
  6201. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  6202. * specified, it is inherited from [chart.type](#chart.type).
  6203. *
  6204. * @extends series,plotOptions.ohlc
  6205. * @excluding dataParser, dataURL
  6206. * @product highstock
  6207. * @apioption series.ohlc
  6208. */
  6209. /**
  6210. * An array of data points for the series. For the `ohlc` series type,
  6211. * points can be given in the following ways:
  6212. *
  6213. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6214. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6215. * as the name of the point, and the `x` value is inferred. The `x` value can
  6216. * also be omitted, in which case the inner arrays should be of length 4\.
  6217. * Then the `x` value is automatically calculated, either starting at 0 and
  6218. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6219. * series options.
  6220. * ```js
  6221. * data: [
  6222. * [0, 6, 5, 6, 7],
  6223. * [1, 9, 4, 8, 2],
  6224. * [2, 6, 3, 4, 10]
  6225. * ]
  6226. * ```
  6227. *
  6228. * 2. An array of objects with named values. The following snippet shows only a
  6229. * few settings, see the complete options set below. If the total number of
  6230. * data points exceeds the series'
  6231. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  6232. * available.
  6233. * ```js
  6234. * data: [{
  6235. * x: 1,
  6236. * open: 3,
  6237. * high: 4,
  6238. * low: 5,
  6239. * close: 2,
  6240. * name: "Point2",
  6241. * color: "#00FF00"
  6242. * }, {
  6243. * x: 1,
  6244. * open: 4,
  6245. * high: 3,
  6246. * low: 6,
  6247. * close: 7,
  6248. * name: "Point1",
  6249. * color: "#FF00FF"
  6250. * }]
  6251. * ```
  6252. *
  6253. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6254. * @extends series.arearange.data
  6255. * @excluding y, marker
  6256. * @product highstock
  6257. * @apioption series.ohlc.data
  6258. */
  6259. /**
  6260. * The closing value of each data point.
  6261. *
  6262. * @type {number}
  6263. * @product highstock
  6264. * @apioption series.ohlc.data.close
  6265. */
  6266. /**
  6267. * The opening value of each data point.
  6268. *
  6269. * @type {number}
  6270. * @product highstock
  6271. * @apioption series.ohlc.data.open
  6272. */
  6273. ''; // adds doclets above to transpilat
  6274. return OHLCSeries;
  6275. });
  6276. _registerModule(_modules, 'Series/Candlestick/CandlestickSeries.js', [_modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (O, palette, SeriesRegistry, U) {
  6277. /* *
  6278. *
  6279. * (c) 2010-2021 Torstein Honsi
  6280. *
  6281. * License: www.highcharts.com/license
  6282. *
  6283. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6284. *
  6285. * */
  6286. var __extends = (this && this.__extends) || (function () {
  6287. var extendStatics = function (d,
  6288. b) {
  6289. extendStatics = Object.setPrototypeOf ||
  6290. ({ __proto__: [] } instanceof Array && function (d,
  6291. b) { d.__proto__ = b; }) ||
  6292. function (d,
  6293. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6294. return extendStatics(d, b);
  6295. };
  6296. return function (d, b) {
  6297. extendStatics(d, b);
  6298. function __() { this.constructor = d; }
  6299. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6300. };
  6301. })();
  6302. var defaultOptions = O.defaultOptions;
  6303. var _a = SeriesRegistry.seriesTypes,
  6304. ColumnSeries = _a.column,
  6305. OHLCSeries = _a.ohlc;
  6306. var merge = U.merge;
  6307. /* *
  6308. *
  6309. * Code
  6310. *
  6311. * */
  6312. /**
  6313. * A candlestick chart is a style of financial chart used to describe price
  6314. * movements over time.
  6315. *
  6316. * @sample stock/demo/candlestick/
  6317. * Candlestick chart
  6318. *
  6319. * @extends plotOptions.ohlc
  6320. * @excluding borderColor,borderRadius,borderWidth
  6321. * @product highstock
  6322. * @optionparent plotOptions.candlestick
  6323. */
  6324. /**
  6325. * The candlestick series type.
  6326. *
  6327. * @private
  6328. * @class
  6329. * @name Highcharts.seriesTypes.candlestick
  6330. *
  6331. * @augments Highcharts.seriesTypes.ohlc
  6332. */
  6333. var CandlestickSeries = /** @class */ (function (_super) {
  6334. __extends(CandlestickSeries, _super);
  6335. function CandlestickSeries() {
  6336. var _this = _super !== null && _super.apply(this,
  6337. arguments) || this;
  6338. /* *
  6339. *
  6340. * Properties
  6341. *
  6342. * */
  6343. _this.data = void 0;
  6344. _this.options = void 0;
  6345. _this.points = void 0;
  6346. return _this;
  6347. }
  6348. /* *
  6349. *
  6350. * Functions
  6351. *
  6352. * */
  6353. /* eslint-disable valid-jsdoc */
  6354. /**
  6355. * Postprocess mapping between options and SVG attributes
  6356. *
  6357. * @private
  6358. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  6359. */
  6360. CandlestickSeries.prototype.pointAttribs = function (point, state) {
  6361. var attribs = ColumnSeries.prototype.pointAttribs.call(this,
  6362. point,
  6363. state),
  6364. options = this.options,
  6365. isUp = point.open < point.close,
  6366. stroke = options.lineColor || this.color,
  6367. color = point.color || this.color, // (#14826)
  6368. stateOptions;
  6369. attribs['stroke-width'] = options.lineWidth;
  6370. attribs.fill = point.options.color ||
  6371. (isUp ? (options.upColor || color) : color);
  6372. attribs.stroke = point.options.lineColor ||
  6373. (isUp ? (options.upLineColor || stroke) : stroke);
  6374. // Select or hover states
  6375. if (state) {
  6376. stateOptions = options.states[state];
  6377. attribs.fill = stateOptions.color || attribs.fill;
  6378. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  6379. attribs['stroke-width'] =
  6380. stateOptions.lineWidth || attribs['stroke-width'];
  6381. }
  6382. return attribs;
  6383. };
  6384. /**
  6385. * Draw the data points.
  6386. *
  6387. * @private
  6388. * @function Highcharts.seriesTypes.candlestick#drawPoints
  6389. * @return {void}
  6390. */
  6391. CandlestickSeries.prototype.drawPoints = function () {
  6392. var series = this,
  6393. points = series.points,
  6394. chart = series.chart,
  6395. reversedYAxis = series.yAxis.reversed;
  6396. points.forEach(function (point) {
  6397. var graphic = point.graphic,
  6398. plotOpen,
  6399. plotClose,
  6400. topBox,
  6401. bottomBox,
  6402. hasTopWhisker,
  6403. hasBottomWhisker,
  6404. crispCorr,
  6405. crispX,
  6406. path,
  6407. halfWidth,
  6408. isNew = !graphic;
  6409. if (typeof point.plotY !== 'undefined') {
  6410. if (!graphic) {
  6411. point.graphic = graphic = chart.renderer.path()
  6412. .add(series.group);
  6413. }
  6414. if (!series.chart.styledMode) {
  6415. graphic
  6416. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  6417. .shadow(series.options.shadow);
  6418. }
  6419. // Crisp vector coordinates
  6420. crispCorr = (graphic.strokeWidth() % 2) / 2;
  6421. // #2596:
  6422. crispX = Math.round(point.plotX) - crispCorr;
  6423. plotOpen = point.plotOpen;
  6424. plotClose = point.plotClose;
  6425. topBox = Math.min(plotOpen, plotClose);
  6426. bottomBox = Math.max(plotOpen, plotClose);
  6427. halfWidth = Math.round(point.shapeArgs.width / 2);
  6428. hasTopWhisker = reversedYAxis ?
  6429. bottomBox !== point.yBottom :
  6430. Math.round(topBox) !==
  6431. Math.round(point.plotHigh);
  6432. hasBottomWhisker = reversedYAxis ?
  6433. Math.round(topBox) !==
  6434. Math.round(point.plotHigh) :
  6435. bottomBox !== point.yBottom;
  6436. topBox = Math.round(topBox) + crispCorr;
  6437. bottomBox = Math.round(bottomBox) + crispCorr;
  6438. // Create the path. Due to a bug in Chrome 49, the path is
  6439. // first instanciated with no values, then the values
  6440. // pushed. For unknown reasons, instanciating the path array
  6441. // with all the values would lead to a crash when updating
  6442. // frequently (#5193).
  6443. path = [];
  6444. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  6445. ['M', crispX, topBox], [
  6446. 'L',
  6447. // #460, #2094
  6448. crispX,
  6449. hasTopWhisker ?
  6450. Math.round(reversedYAxis ?
  6451. point.yBottom :
  6452. point.plotHigh) :
  6453. topBox
  6454. ], ['M', crispX, bottomBox], [
  6455. 'L',
  6456. // #460, #2094
  6457. crispX,
  6458. hasBottomWhisker ?
  6459. Math.round(reversedYAxis ?
  6460. point.plotHigh :
  6461. point.yBottom) :
  6462. bottomBox
  6463. ]);
  6464. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6465. .addClass(point.getClassName(), true);
  6466. }
  6467. });
  6468. /* eslint-enable valid-jsdoc */
  6469. };
  6470. /* *
  6471. *
  6472. * Static properties
  6473. *
  6474. * */
  6475. CandlestickSeries.defaultOptions = merge(OHLCSeries.defaultOptions, defaultOptions.plotOptions, {
  6476. /**
  6477. * The specific line color for up candle sticks. The default is to
  6478. * inherit the general `lineColor` setting.
  6479. *
  6480. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6481. * Candlestick line colors
  6482. *
  6483. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6484. * @since 1.3.6
  6485. * @product highstock
  6486. * @apioption plotOptions.candlestick.upLineColor
  6487. */
  6488. /**
  6489. * @type {Highcharts.DataGroupingApproximationValue|Function}
  6490. * @default ohlc
  6491. * @product highstock
  6492. * @apioption plotOptions.candlestick.dataGrouping.approximation
  6493. */
  6494. states: {
  6495. /**
  6496. * @extends plotOptions.column.states.hover
  6497. * @product highstock
  6498. */
  6499. hover: {
  6500. /**
  6501. * The pixel width of the line/border around the candlestick.
  6502. *
  6503. * @product highstock
  6504. */
  6505. lineWidth: 2
  6506. }
  6507. },
  6508. /**
  6509. * @extends plotOptions.ohlc.tooltip
  6510. */
  6511. tooltip: defaultOptions.plotOptions.ohlc.tooltip,
  6512. /**
  6513. * @type {number|null}
  6514. * @product highstock
  6515. */
  6516. threshold: null,
  6517. /**
  6518. * The color of the line/border of the candlestick.
  6519. *
  6520. * In styled mode, the line stroke can be set with the
  6521. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6522. *
  6523. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  6524. *
  6525. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6526. * Candlestick line colors
  6527. *
  6528. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6529. * @default #000000
  6530. * @product highstock
  6531. */
  6532. lineColor: palette.neutralColor100,
  6533. /**
  6534. * The pixel width of the candlestick line/border. Defaults to `1`.
  6535. *
  6536. *
  6537. * In styled mode, the line stroke width can be set with the
  6538. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6539. *
  6540. * @product highstock
  6541. */
  6542. lineWidth: 1,
  6543. /**
  6544. * The fill color of the candlestick when values are rising.
  6545. *
  6546. * In styled mode, the up color can be set with the
  6547. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  6548. *
  6549. * @sample {highstock} stock/plotoptions/candlestick-color/
  6550. * Custom colors
  6551. * @sample {highstock} highcharts/css/candlestick/
  6552. * Colors in styled mode
  6553. *
  6554. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6555. * @default #ffffff
  6556. * @product highstock
  6557. */
  6558. upColor: palette.backgroundColor,
  6559. /**
  6560. * @product highstock
  6561. */
  6562. stickyTracking: true
  6563. });
  6564. return CandlestickSeries;
  6565. }(OHLCSeries));
  6566. SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
  6567. /* *
  6568. *
  6569. * Default Export
  6570. *
  6571. * */
  6572. /* *
  6573. *
  6574. * API Options
  6575. *
  6576. * */
  6577. /**
  6578. * A `candlestick` series. If the [type](#series.candlestick.type)
  6579. * option is not specified, it is inherited from [chart.type](
  6580. * #chart.type).
  6581. *
  6582. * @type {*}
  6583. * @extends series,plotOptions.candlestick
  6584. * @excluding dataParser, dataURL
  6585. * @product highstock
  6586. * @apioption series.candlestick
  6587. */
  6588. /**
  6589. * An array of data points for the series. For the `candlestick` series
  6590. * type, points can be given in the following ways:
  6591. *
  6592. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6593. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6594. * as the name of the point, and the `x` value is inferred. The `x` value can
  6595. * also be omitted, in which case the inner arrays should be of length 4.
  6596. * Then the `x` value is automatically calculated, either starting at 0 and
  6597. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6598. * series options.
  6599. * ```js
  6600. * data: [
  6601. * [0, 7, 2, 0, 4],
  6602. * [1, 1, 4, 2, 8],
  6603. * [2, 3, 3, 9, 3]
  6604. * ]
  6605. * ```
  6606. *
  6607. * 2. An array of objects with named values. The following snippet shows only a
  6608. * few settings, see the complete options set below. If the total number of
  6609. * data points exceeds the series'
  6610. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  6611. * available.
  6612. * ```js
  6613. * data: [{
  6614. * x: 1,
  6615. * open: 9,
  6616. * high: 2,
  6617. * low: 4,
  6618. * close: 6,
  6619. * name: "Point2",
  6620. * color: "#00FF00"
  6621. * }, {
  6622. * x: 1,
  6623. * open: 1,
  6624. * high: 4,
  6625. * low: 7,
  6626. * close: 7,
  6627. * name: "Point1",
  6628. * color: "#FF00FF"
  6629. * }]
  6630. * ```
  6631. *
  6632. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6633. * @extends series.ohlc.data
  6634. * @excluding y
  6635. * @product highstock
  6636. * @apioption series.candlestick.data
  6637. */
  6638. ''; // adds doclets above to transpilat
  6639. return CandlestickSeries;
  6640. });
  6641. _registerModule(_modules, 'Series/Flags/FlagsPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  6642. /* *
  6643. *
  6644. * (c) 2010-2021 Torstein Honsi
  6645. *
  6646. * License: www.highcharts.com/license
  6647. *
  6648. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6649. *
  6650. * */
  6651. var __extends = (this && this.__extends) || (function () {
  6652. var extendStatics = function (d,
  6653. b) {
  6654. extendStatics = Object.setPrototypeOf ||
  6655. ({ __proto__: [] } instanceof Array && function (d,
  6656. b) { d.__proto__ = b; }) ||
  6657. function (d,
  6658. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6659. return extendStatics(d, b);
  6660. };
  6661. return function (d, b) {
  6662. extendStatics(d, b);
  6663. function __() { this.constructor = d; }
  6664. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6665. };
  6666. })();
  6667. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6668. var isNumber = U.isNumber;
  6669. /* *
  6670. *
  6671. * Class
  6672. *
  6673. * */
  6674. var FlagsPoint = /** @class */ (function (_super) {
  6675. __extends(FlagsPoint, _super);
  6676. function FlagsPoint() {
  6677. /* *
  6678. *
  6679. * Properties
  6680. *
  6681. * */
  6682. var _this = _super !== null && _super.apply(this,
  6683. arguments) || this;
  6684. _this.options = void 0;
  6685. _this.series = void 0;
  6686. return _this;
  6687. }
  6688. /* *
  6689. *
  6690. * Functions
  6691. *
  6692. * */
  6693. /* eslint-disable valid-jsdoc */
  6694. /**
  6695. * @private
  6696. */
  6697. FlagsPoint.prototype.isValid = function () {
  6698. // #9233 - Prevent from treating flags as null points (even if
  6699. // they have no y values defined).
  6700. return isNumber(this.y) || typeof this.y === 'undefined';
  6701. };
  6702. return FlagsPoint;
  6703. }(ColumnSeries.prototype.pointClass));
  6704. /* *
  6705. *
  6706. * Default Export
  6707. *
  6708. * */
  6709. return FlagsPoint;
  6710. });
  6711. _registerModule(_modules, 'Mixins/OnSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (ColumnSeries, Series, U) {
  6712. /* *
  6713. *
  6714. * (c) 2010-2021 Torstein Honsi
  6715. *
  6716. * License: www.highcharts.com/license
  6717. *
  6718. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6719. *
  6720. * */
  6721. var columnProto = ColumnSeries.prototype;
  6722. var seriesProto = Series.prototype;
  6723. var defined = U.defined,
  6724. stableSort = U.stableSort;
  6725. /**
  6726. * @private
  6727. * @mixin onSeriesMixin
  6728. */
  6729. var onSeriesMixin = {
  6730. /* eslint-disable valid-jsdoc */
  6731. /**
  6732. * Override getPlotBox. If the onSeries option is valid,
  6733. return the plot box
  6734. * of the onSeries,
  6735. otherwise proceed as usual.
  6736. *
  6737. * @private
  6738. * @function onSeriesMixin.getPlotBox
  6739. * @return {Highcharts.SeriesPlotBoxObject}
  6740. */
  6741. getPlotBox: function () {
  6742. return seriesProto.getPlotBox.call((this.options.onSeries &&
  6743. this.chart.get(this.options.onSeries)) || this);
  6744. },
  6745. /**
  6746. * Extend the translate method by placing the point on the related series
  6747. *
  6748. * @private
  6749. * @function onSeriesMixin.translate
  6750. * @return {void}
  6751. */
  6752. translate: function () {
  6753. columnProto.translate.apply(this);
  6754. var series = this,
  6755. options = series.options,
  6756. chart = series.chart,
  6757. points = series.points,
  6758. cursor = points.length - 1,
  6759. point,
  6760. lastPoint,
  6761. optionsOnSeries = options.onSeries,
  6762. onSeries = (optionsOnSeries &&
  6763. chart.get(optionsOnSeries)),
  6764. onKey = options.onKey || 'y',
  6765. step = onSeries && onSeries.options.step,
  6766. onData = (onSeries && onSeries.points),
  6767. i = onData && onData.length,
  6768. inverted = chart.inverted,
  6769. xAxis = series.xAxis,
  6770. yAxis = series.yAxis,
  6771. xOffset = 0,
  6772. leftPoint,
  6773. lastX,
  6774. rightPoint,
  6775. currentDataGrouping,
  6776. distanceRatio;
  6777. // relate to a master series
  6778. if (onSeries && onSeries.visible && i) {
  6779. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  6780. currentDataGrouping = onSeries.currentDataGrouping;
  6781. lastX = (onData[i - 1].x +
  6782. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  6783. // sort the data points
  6784. stableSort(points, function (a, b) {
  6785. return (a.x - b.x);
  6786. });
  6787. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  6788. while (i-- && points[cursor]) {
  6789. leftPoint = onData[i];
  6790. point = points[cursor];
  6791. point.y = leftPoint.y;
  6792. if (leftPoint.x <= point.x &&
  6793. typeof leftPoint[onKey] !== 'undefined') {
  6794. if (point.x <= lastX) { // #803
  6795. point.plotY = leftPoint[onKey];
  6796. // interpolate between points, #666
  6797. if (leftPoint.x < point.x &&
  6798. !step) {
  6799. rightPoint = onData[i + 1];
  6800. if (rightPoint &&
  6801. typeof rightPoint[onKey] !== 'undefined') {
  6802. // the distance ratio, between 0 and 1
  6803. distanceRatio =
  6804. (point.x - leftPoint.x) /
  6805. (rightPoint.x - leftPoint.x);
  6806. point.plotY +=
  6807. distanceRatio *
  6808. // the plotY distance
  6809. (rightPoint[onKey] - leftPoint[onKey]);
  6810. point.y +=
  6811. distanceRatio *
  6812. (rightPoint.y - leftPoint.y);
  6813. }
  6814. }
  6815. }
  6816. cursor--;
  6817. i++; // check again for points in the same x position
  6818. if (cursor < 0) {
  6819. break;
  6820. }
  6821. }
  6822. }
  6823. }
  6824. // Add plotY position and handle stacking
  6825. points.forEach(function (point, i) {
  6826. var stackIndex;
  6827. point.plotX += xOffset; // #2049
  6828. // Undefined plotY means the point is either on axis, outside series
  6829. // range or hidden series. If the series is outside the range of the
  6830. // x axis it should fall through with an undefined plotY, but then
  6831. // we must remove the shapeArgs (#847). For inverted charts, we need
  6832. // to calculate position anyway, because series.invertGroups is not
  6833. // defined
  6834. if (typeof point.plotY === 'undefined' || inverted) {
  6835. if (point.plotX >= 0 &&
  6836. point.plotX <= xAxis.len) {
  6837. // We're inside xAxis range
  6838. if (inverted) {
  6839. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  6840. point.plotX = defined(point.y) ?
  6841. yAxis.translate(point.y, 0, 0, 0, 1) :
  6842. 0;
  6843. }
  6844. else {
  6845. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  6846. xAxis.offset; // For the windbarb demo
  6847. }
  6848. }
  6849. else {
  6850. point.shapeArgs = {}; // 847
  6851. }
  6852. }
  6853. // if multiple flags appear at the same x, order them into a stack
  6854. lastPoint = points[i - 1];
  6855. if (lastPoint && lastPoint.plotX === point.plotX) {
  6856. if (typeof lastPoint.stackIndex === 'undefined') {
  6857. lastPoint.stackIndex = 0;
  6858. }
  6859. stackIndex = lastPoint.stackIndex + 1;
  6860. }
  6861. point.stackIndex = stackIndex; // #3639
  6862. });
  6863. this.onSeries = onSeries;
  6864. }
  6865. /* eslint-enable valid-jsdoc */
  6866. };
  6867. return onSeriesMixin;
  6868. });
  6869. _registerModule(_modules, 'Series/Flags/FlagsSymbols.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGRenderer.js']], function (H, SVGRenderer) {
  6870. /* *
  6871. *
  6872. * Imports
  6873. *
  6874. * */
  6875. var Renderer = H.Renderer,
  6876. VMLRenderer = H.VMLRenderer;
  6877. var symbols = SVGRenderer.prototype.symbols;
  6878. /* *
  6879. *
  6880. * Symbols
  6881. *
  6882. * */
  6883. // create the flag icon with anchor
  6884. symbols.flag = function (x, y, w, h, options) {
  6885. var anchorX = (options && options.anchorX) || x,
  6886. anchorY = (options && options.anchorY) || y;
  6887. // To do: unwanted any cast because symbols.circle has wrong type, it
  6888. // actually returns an SVGPathArray
  6889. var path = symbols.circle(anchorX - 1,
  6890. anchorY - 1, 2, 2);
  6891. 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']);
  6892. return path;
  6893. };
  6894. /**
  6895. * Create the circlepin and squarepin icons with anchor.
  6896. * @private
  6897. * @param {string} shape - circle or square
  6898. * @return {void}
  6899. */
  6900. function createPinSymbol(shape) {
  6901. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  6902. var anchorX = options && options.anchorX,
  6903. anchorY = options && options.anchorY,
  6904. path;
  6905. // For single-letter flags, make sure circular flags are not taller
  6906. // than their width
  6907. if (shape === 'circle' && h > w) {
  6908. x -= Math.round((h - w) / 2);
  6909. w = h;
  6910. }
  6911. path = (symbols[shape])(x, y, w, h);
  6912. if (anchorX && anchorY) {
  6913. /**
  6914. * If the label is below the anchor, draw the connecting line from
  6915. * the top edge of the label, otherwise start drawing from the
  6916. * bottom edge
  6917. */
  6918. var labelX = anchorX;
  6919. if (shape === 'circle') {
  6920. labelX = x + w / 2;
  6921. }
  6922. else {
  6923. var startSeg = path[0];
  6924. var endSeg = path[1];
  6925. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  6926. labelX = (startSeg[1] + endSeg[1]) / 2;
  6927. }
  6928. }
  6929. var labelY = (y > anchorY) ? y : y + h;
  6930. path.push([
  6931. 'M',
  6932. labelX,
  6933. labelY
  6934. ], [
  6935. 'L',
  6936. anchorX,
  6937. anchorY
  6938. ]);
  6939. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  6940. }
  6941. return path;
  6942. };
  6943. }
  6944. createPinSymbol('circle');
  6945. createPinSymbol('square');
  6946. /**
  6947. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  6948. * Even VML browsers need this in order to generate shapes in export. Now share
  6949. * them with the VMLRenderer.
  6950. */
  6951. if (Renderer === VMLRenderer) {
  6952. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  6953. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  6954. });
  6955. }
  6956. /* *
  6957. *
  6958. * Default Export
  6959. *
  6960. * */
  6961. return symbols;
  6962. });
  6963. _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) {
  6964. /* *
  6965. *
  6966. * (c) 2010-2021 Torstein Honsi
  6967. *
  6968. * License: www.highcharts.com/license
  6969. *
  6970. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6971. *
  6972. * */
  6973. var __extends = (this && this.__extends) || (function () {
  6974. var extendStatics = function (d,
  6975. b) {
  6976. extendStatics = Object.setPrototypeOf ||
  6977. ({ __proto__: [] } instanceof Array && function (d,
  6978. b) { d.__proto__ = b; }) ||
  6979. function (d,
  6980. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6981. return extendStatics(d, b);
  6982. };
  6983. return function (d, b) {
  6984. extendStatics(d, b);
  6985. function __() { this.constructor = d; }
  6986. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6987. };
  6988. })();
  6989. var noop = H.noop;
  6990. var Series = SeriesRegistry.series,
  6991. ColumnSeries = SeriesRegistry.seriesTypes.column;
  6992. var addEvent = U.addEvent,
  6993. defined = U.defined,
  6994. extend = U.extend,
  6995. merge = U.merge,
  6996. objectEach = U.objectEach,
  6997. wrap = U.wrap;
  6998. /**
  6999. * The Flags series.
  7000. *
  7001. * @private
  7002. * @class
  7003. * @name Highcharts.seriesTypes.flags
  7004. *
  7005. * @augments Highcharts.Series
  7006. */
  7007. var FlagsSeries = /** @class */ (function (_super) {
  7008. __extends(FlagsSeries, _super);
  7009. function FlagsSeries() {
  7010. /* *
  7011. *
  7012. * Static Properties
  7013. *
  7014. * */
  7015. var _this = _super !== null && _super.apply(this,
  7016. arguments) || this;
  7017. /* *
  7018. *
  7019. * Properties
  7020. *
  7021. * */
  7022. _this.data = void 0;
  7023. _this.options = void 0;
  7024. _this.points = void 0;
  7025. return _this;
  7026. /* eslint-enable valid-jsdoc */
  7027. }
  7028. /* *
  7029. *
  7030. * Functions
  7031. *
  7032. * */
  7033. /* eslint-disable valid-jsdoc */
  7034. /**
  7035. * Disable animation, but keep clipping (#8546).
  7036. * @private
  7037. */
  7038. FlagsSeries.prototype.animate = function (init) {
  7039. if (init) {
  7040. this.setClip();
  7041. }
  7042. };
  7043. /**
  7044. * Draw the markers.
  7045. * @private
  7046. */
  7047. FlagsSeries.prototype.drawPoints = function () {
  7048. var series = this,
  7049. points = series.points,
  7050. chart = series.chart,
  7051. renderer = chart.renderer,
  7052. plotX,
  7053. plotY,
  7054. inverted = chart.inverted,
  7055. options = series.options,
  7056. optionsY = options.y,
  7057. shape,
  7058. i,
  7059. point,
  7060. graphic,
  7061. stackIndex,
  7062. anchorY,
  7063. attribs,
  7064. outsideRight,
  7065. yAxis = series.yAxis,
  7066. boxesMap = {},
  7067. boxes = [],
  7068. centered;
  7069. i = points.length;
  7070. while (i--) {
  7071. point = points[i];
  7072. outsideRight =
  7073. (inverted ? point.plotY : point.plotX) >
  7074. series.xAxis.len;
  7075. plotX = point.plotX;
  7076. stackIndex = point.stackIndex;
  7077. shape = point.options.shape || options.shape;
  7078. plotY = point.plotY;
  7079. if (typeof plotY !== 'undefined') {
  7080. plotY = point.plotY + optionsY -
  7081. (typeof stackIndex !== 'undefined' &&
  7082. (stackIndex * options.stackDistance));
  7083. }
  7084. // skip connectors for higher level stacked points
  7085. point.anchorX = stackIndex ? void 0 : point.plotX;
  7086. anchorY = stackIndex ? void 0 : point.plotY;
  7087. centered = shape !== 'flag';
  7088. graphic = point.graphic;
  7089. // Only draw the point if y is defined and the flag is within
  7090. // the visible area
  7091. if (typeof plotY !== 'undefined' &&
  7092. plotX >= 0 &&
  7093. !outsideRight) {
  7094. // Create the flag
  7095. if (!graphic) {
  7096. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML)
  7097. .addClass('highcharts-point')
  7098. .add(series.markerGroup);
  7099. // Add reference to the point for tracker (#6303)
  7100. if (point.graphic.div) {
  7101. point.graphic.div.point = point;
  7102. }
  7103. graphic.isNew = true;
  7104. }
  7105. graphic.attr({
  7106. align: centered ? 'center' : 'left',
  7107. width: options.width,
  7108. height: options.height,
  7109. 'text-align': options.textAlign
  7110. });
  7111. if (!chart.styledMode) {
  7112. graphic
  7113. .attr(series.pointAttribs(point))
  7114. .css(merge(options.style, point.style))
  7115. .shadow(options.shadow);
  7116. }
  7117. if (plotX > 0) { // #3119
  7118. plotX -= graphic.strokeWidth() % 2; // #4285
  7119. }
  7120. // Plant the flag
  7121. attribs = {
  7122. y: plotY,
  7123. anchorY: anchorY
  7124. };
  7125. if (options.allowOverlapX) {
  7126. attribs.x = plotX;
  7127. attribs.anchorX = point.anchorX;
  7128. }
  7129. graphic.attr({
  7130. text: point.options.title || options.title || 'A'
  7131. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  7132. // Rig for the distribute function
  7133. if (!options.allowOverlapX) {
  7134. if (!boxesMap[point.plotX]) {
  7135. boxesMap[point.plotX] = {
  7136. align: centered ? 0.5 : 0,
  7137. size: graphic.width,
  7138. target: plotX,
  7139. anchorX: plotX
  7140. };
  7141. }
  7142. else {
  7143. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  7144. }
  7145. }
  7146. // Set the tooltip anchor position
  7147. point.tooltipPos = [
  7148. plotX,
  7149. plotY + yAxis.pos - chart.plotTop
  7150. ]; // #6327
  7151. }
  7152. else if (graphic) {
  7153. point.graphic = graphic.destroy();
  7154. }
  7155. }
  7156. // Handle X-dimension overlapping
  7157. if (!options.allowOverlapX) {
  7158. objectEach(boxesMap, function (box) {
  7159. box.plotX = box.anchorX;
  7160. boxes.push(box);
  7161. });
  7162. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  7163. points.forEach(function (point) {
  7164. var box = point.graphic && boxesMap[point.plotX];
  7165. if (box) {
  7166. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  7167. x: box.pos + box.align * box.size,
  7168. anchorX: point.anchorX
  7169. });
  7170. // Hide flag when its box position is not specified
  7171. // (#8573, #9299)
  7172. if (!defined(box.pos)) {
  7173. point.graphic.attr({
  7174. x: -9999,
  7175. anchorX: -9999
  7176. });
  7177. point.graphic.isNew = true;
  7178. }
  7179. else {
  7180. point.graphic.isNew = false;
  7181. }
  7182. }
  7183. });
  7184. }
  7185. // Can be a mix of SVG and HTML and we need events for both (#6303)
  7186. if (options.useHTML) {
  7187. wrap(series.markerGroup, 'on', function (proceed) {
  7188. return SVGElement.prototype.on.apply(
  7189. // for HTML
  7190. // eslint-disable-next-line no-invalid-this
  7191. proceed.apply(this, [].slice.call(arguments, 1)),
  7192. // and for SVG
  7193. [].slice.call(arguments, 1));
  7194. });
  7195. }
  7196. };
  7197. /**
  7198. * Extend the column trackers with listeners to expand and contract
  7199. * stacks.
  7200. * @private
  7201. */
  7202. FlagsSeries.prototype.drawTracker = function () {
  7203. var series = this,
  7204. points = series.points;
  7205. _super.prototype.drawTracker.call(this);
  7206. /* *
  7207. * Bring each stacked flag up on mouse over, this allows readability
  7208. * of vertically stacked elements as well as tight points on the x
  7209. * axis. #1924.
  7210. */
  7211. points.forEach(function (point) {
  7212. var graphic = point.graphic;
  7213. if (graphic) {
  7214. addEvent(graphic.element, 'mouseover', function () {
  7215. // Raise this point
  7216. if (point.stackIndex > 0 &&
  7217. !point.raised) {
  7218. point._y = graphic.y;
  7219. graphic.attr({
  7220. y: point._y - 8
  7221. });
  7222. point.raised = true;
  7223. }
  7224. // Revert other raised points
  7225. points.forEach(function (otherPoint) {
  7226. if (otherPoint !== point &&
  7227. otherPoint.raised &&
  7228. otherPoint.graphic) {
  7229. otherPoint.graphic.attr({
  7230. y: otherPoint._y
  7231. });
  7232. otherPoint.raised = false;
  7233. }
  7234. });
  7235. });
  7236. }
  7237. });
  7238. };
  7239. /**
  7240. * Get presentational attributes
  7241. * @private
  7242. */
  7243. FlagsSeries.prototype.pointAttribs = function (point, state) {
  7244. var options = this.options,
  7245. color = (point && point.color) || this.color,
  7246. lineColor = options.lineColor,
  7247. lineWidth = (point && point.lineWidth),
  7248. fill = (point && point.fillColor) || options.fillColor;
  7249. if (state) {
  7250. fill = options.states[state].fillColor;
  7251. lineColor = options.states[state].lineColor;
  7252. lineWidth = options.states[state].lineWidth;
  7253. }
  7254. return {
  7255. fill: fill || color,
  7256. stroke: lineColor || color,
  7257. 'stroke-width': lineWidth || options.lineWidth || 0
  7258. };
  7259. };
  7260. /**
  7261. * @private
  7262. */
  7263. FlagsSeries.prototype.setClip = function () {
  7264. Series.prototype.setClip.apply(this, arguments);
  7265. if (this.options.clip !== false && this.sharedClipKey) {
  7266. this.markerGroup
  7267. .clip(this.chart[this.sharedClipKey]);
  7268. }
  7269. };
  7270. /**
  7271. * Flags are used to mark events in stock charts. They can be added on the
  7272. * timeline, or attached to a specific series.
  7273. *
  7274. * @sample stock/demo/flags-general/
  7275. * Flags on a line series
  7276. *
  7277. * @extends plotOptions.column
  7278. * @excluding animation, borderColor, borderRadius, borderWidth,
  7279. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  7280. * turboThreshold
  7281. * @product highstock
  7282. * @optionparent plotOptions.flags
  7283. */
  7284. FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  7285. /**
  7286. * In case the flag is placed on a series, on what point key to place
  7287. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  7288. * however, the flag can optionally be placed on the `open`, `high`,
  7289. * `low` or `close` key.
  7290. *
  7291. * @sample {highstock} stock/plotoptions/flags-onkey/
  7292. * Range series, flag on high
  7293. *
  7294. * @type {string}
  7295. * @default y
  7296. * @since 4.2.2
  7297. * @product highstock
  7298. * @validvalue ["y", "open", "high", "low", "close"]
  7299. * @apioption plotOptions.flags.onKey
  7300. */
  7301. /**
  7302. * The id of the series that the flags should be drawn on. If no id
  7303. * is given, the flags are drawn on the x axis.
  7304. *
  7305. * @sample {highstock} stock/plotoptions/flags/
  7306. * Flags on series and on x axis
  7307. *
  7308. * @type {string}
  7309. * @product highstock
  7310. * @apioption plotOptions.flags.onSeries
  7311. */
  7312. pointRange: 0,
  7313. /**
  7314. * Whether the flags are allowed to overlap sideways. If `false`, the
  7315. * flags are moved sideways using an algorithm that seeks to place every
  7316. * flag as close as possible to its original position.
  7317. *
  7318. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  7319. * Allow sideways overlap
  7320. *
  7321. * @since 6.0.4
  7322. */
  7323. allowOverlapX: false,
  7324. /**
  7325. * The shape of the marker. Can be one of "flag", "circlepin",
  7326. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  7327. * Individual shapes can also be set for each point.
  7328. *
  7329. * @sample {highstock} stock/plotoptions/flags/
  7330. * Different shapes
  7331. *
  7332. * @type {Highcharts.FlagsShapeValue}
  7333. * @product highstock
  7334. */
  7335. shape: 'flag',
  7336. /**
  7337. * When multiple flags in the same series fall on the same value, this
  7338. * number determines the vertical offset between them.
  7339. *
  7340. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  7341. * A greater stack distance
  7342. *
  7343. * @product highstock
  7344. */
  7345. stackDistance: 12,
  7346. /**
  7347. * Text alignment for the text inside the flag.
  7348. *
  7349. * @since 5.0.0
  7350. * @product highstock
  7351. * @validvalue ["left", "center", "right"]
  7352. */
  7353. textAlign: 'center',
  7354. /**
  7355. * Specific tooltip options for flag series. Flag series tooltips are
  7356. * different from most other types in that a flag doesn't have a data
  7357. * value, so the tooltip rather displays the `text` option for each
  7358. * point.
  7359. *
  7360. * @extends plotOptions.series.tooltip
  7361. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  7362. * @product highstock
  7363. */
  7364. tooltip: {
  7365. pointFormat: '{point.text}<br/>'
  7366. },
  7367. threshold: null,
  7368. /**
  7369. * The text to display on each flag. This can be defined on series
  7370. * level, or individually for each point. Defaults to `"A"`.
  7371. *
  7372. * @type {string}
  7373. * @default A
  7374. * @product highstock
  7375. * @apioption plotOptions.flags.title
  7376. */
  7377. /**
  7378. * The y position of the top left corner of the flag relative to either
  7379. * the series (if onSeries is defined), or the x axis. Defaults to
  7380. * `-30`.
  7381. *
  7382. * @product highstock
  7383. */
  7384. y: -30,
  7385. /**
  7386. * Whether to use HTML to render the flag texts. Using HTML allows for
  7387. * advanced formatting, images and reliable bi-directional text
  7388. * rendering. Note that exported images won't respect the HTML, and that
  7389. * HTML won't respect Z-index settings.
  7390. *
  7391. * @type {boolean}
  7392. * @default false
  7393. * @since 1.3
  7394. * @product highstock
  7395. * @apioption plotOptions.flags.useHTML
  7396. */
  7397. /**
  7398. * Fixed width of the flag's shape. By default, width is autocalculated
  7399. * according to the flag's title.
  7400. *
  7401. * @sample {highstock} stock/demo/flags-shapes/
  7402. * Flags with fixed width
  7403. *
  7404. * @type {number}
  7405. * @product highstock
  7406. * @apioption plotOptions.flags.width
  7407. */
  7408. /**
  7409. * Fixed height of the flag's shape. By default, height is
  7410. * autocalculated according to the flag's title.
  7411. *
  7412. * @type {number}
  7413. * @product highstock
  7414. * @apioption plotOptions.flags.height
  7415. */
  7416. /**
  7417. * The fill color for the flags.
  7418. *
  7419. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7420. * @product highstock
  7421. */
  7422. fillColor: palette.backgroundColor,
  7423. /**
  7424. * The color of the line/border of the flag.
  7425. *
  7426. * In styled mode, the stroke is set in the
  7427. * `.highcharts-flag-series.highcharts-point` rule.
  7428. *
  7429. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7430. * @default #000000
  7431. * @product highstock
  7432. * @apioption plotOptions.flags.lineColor
  7433. */
  7434. /**
  7435. * The pixel width of the flag's line/border.
  7436. *
  7437. * @product highstock
  7438. */
  7439. lineWidth: 1,
  7440. states: {
  7441. /**
  7442. * @extends plotOptions.column.states.hover
  7443. * @product highstock
  7444. */
  7445. hover: {
  7446. /**
  7447. * The color of the line/border of the flag.
  7448. *
  7449. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7450. * @product highstock
  7451. */
  7452. lineColor: palette.neutralColor100,
  7453. /**
  7454. * The fill or background color of the flag.
  7455. *
  7456. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7457. * @product highstock
  7458. */
  7459. fillColor: palette.highlightColor20
  7460. }
  7461. },
  7462. /**
  7463. * The text styles of the flag.
  7464. *
  7465. * In styled mode, the styles are set in the
  7466. * `.highcharts-flag-series .highcharts-point` rule.
  7467. *
  7468. * @type {Highcharts.CSSObject}
  7469. * @default {"fontSize": "11px", "fontWeight": "bold"}
  7470. * @product highstock
  7471. */
  7472. style: {
  7473. /** @ignore-option */
  7474. fontSize: '11px',
  7475. /** @ignore-option */
  7476. fontWeight: 'bold'
  7477. }
  7478. });
  7479. return FlagsSeries;
  7480. }(ColumnSeries));
  7481. extend(FlagsSeries.prototype, {
  7482. allowDG: false,
  7483. /**
  7484. * @private
  7485. * @function Highcharts.seriesTypes.flags#buildKDTree
  7486. */
  7487. buildKDTree: noop,
  7488. forceCrop: true,
  7489. getPlotBox: OnSeriesMixin.getPlotBox,
  7490. /**
  7491. * Inherit the initialization from base Series.
  7492. *
  7493. * @private
  7494. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  7495. */
  7496. init: Series.prototype.init,
  7497. /**
  7498. * Don't invert the flag marker group (#4960).
  7499. *
  7500. * @private
  7501. * @function Highcharts.seriesTypes.flags#invertGroups
  7502. */
  7503. invertGroups: noop,
  7504. // Flags series group should not be invertible (#14063).
  7505. invertible: false,
  7506. noSharedTooltip: true,
  7507. pointClass: FlagsPoint,
  7508. sorted: false,
  7509. takeOrdinalPosition: false,
  7510. trackerGroups: ['markerGroup'],
  7511. translate: OnSeriesMixin.translate
  7512. });
  7513. SeriesRegistry.registerSeriesType('flags', FlagsSeries);
  7514. /* *
  7515. *
  7516. * Default Export
  7517. *
  7518. * */
  7519. /* *
  7520. *
  7521. * API Declarations
  7522. *
  7523. * */
  7524. /**
  7525. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  7526. */
  7527. ''; // detach doclets above
  7528. /* *
  7529. *
  7530. * API Option
  7531. *
  7532. * */
  7533. /**
  7534. * A `flags` series. If the [type](#series.flags.type) option is not
  7535. * specified, it is inherited from [chart.type](#chart.type).
  7536. *
  7537. * @extends series,plotOptions.flags
  7538. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  7539. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  7540. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  7541. * pointWidth, step, turboThreshold, useOhlcData
  7542. * @product highstock
  7543. * @apioption series.flags
  7544. */
  7545. /**
  7546. * An array of data points for the series. For the `flags` series type,
  7547. * points can be given in the following ways:
  7548. *
  7549. * 1. An array of objects with named values. The following snippet shows only a
  7550. * few settings, see the complete options set below. If the total number of
  7551. * data points exceeds the series'
  7552. * [turboThreshold](#series.flags.turboThreshold), this option is not
  7553. * available.
  7554. * ```js
  7555. * data: [{
  7556. * x: 1,
  7557. * title: "A",
  7558. * text: "First event"
  7559. * }, {
  7560. * x: 1,
  7561. * title: "B",
  7562. * text: "Second event"
  7563. * }]
  7564. * ```
  7565. *
  7566. * @type {Array<*>}
  7567. * @extends series.line.data
  7568. * @excluding dataLabels, marker, name, y
  7569. * @product highstock
  7570. * @apioption series.flags.data
  7571. */
  7572. /**
  7573. * The fill color of an individual flag. By default it inherits from
  7574. * the series color.
  7575. *
  7576. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7577. * @product highstock
  7578. * @apioption series.flags.data.fillColor
  7579. */
  7580. /**
  7581. * The longer text to be shown in the flag's tooltip.
  7582. *
  7583. * @type {string}
  7584. * @product highstock
  7585. * @apioption series.flags.data.text
  7586. */
  7587. /**
  7588. * The short text to be shown on the flag.
  7589. *
  7590. * @type {string}
  7591. * @product highstock
  7592. * @apioption series.flags.data.title
  7593. */
  7594. ''; // adds doclets above to transpiled file
  7595. return FlagsSeries;
  7596. });
  7597. _registerModule(_modules, 'Extensions/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, O, palette, SVGElement, U) {
  7598. /* *
  7599. *
  7600. * (c) 2010-2021 Torstein Honsi
  7601. *
  7602. * License: www.highcharts.com/license
  7603. *
  7604. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7605. *
  7606. * */
  7607. var defaultOptions = O.defaultOptions;
  7608. var addEvent = U.addEvent,
  7609. createElement = U.createElement,
  7610. css = U.css,
  7611. defined = U.defined,
  7612. destroyObjectProperties = U.destroyObjectProperties,
  7613. discardElement = U.discardElement,
  7614. extend = U.extend,
  7615. find = U.find,
  7616. fireEvent = U.fireEvent,
  7617. isNumber = U.isNumber,
  7618. merge = U.merge,
  7619. objectEach = U.objectEach,
  7620. pad = U.pad,
  7621. pick = U.pick,
  7622. pInt = U.pInt,
  7623. splat = U.splat;
  7624. /**
  7625. * Define the time span for the button
  7626. *
  7627. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  7628. */
  7629. /**
  7630. * Callback function to react on button clicks.
  7631. *
  7632. * @callback Highcharts.RangeSelectorClickCallbackFunction
  7633. *
  7634. * @param {global.Event} e
  7635. * Event arguments.
  7636. *
  7637. * @param {boolean|undefined}
  7638. * Return false to cancel the default button event.
  7639. */
  7640. /**
  7641. * Callback function to parse values entered in the input boxes and return a
  7642. * valid JavaScript time as milliseconds since 1970.
  7643. *
  7644. * @callback Highcharts.RangeSelectorParseCallbackFunction
  7645. *
  7646. * @param {string} value
  7647. * Input value to parse.
  7648. *
  7649. * @return {number}
  7650. * Parsed JavaScript time value.
  7651. */
  7652. /* ************************************************************************** *
  7653. * Start Range Selector code *
  7654. * ************************************************************************** */
  7655. extend(defaultOptions, {
  7656. /**
  7657. * The range selector is a tool for selecting ranges to display within
  7658. * the chart. It provides buttons to select preconfigured ranges in
  7659. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  7660. * boxes where min and max dates can be manually input.
  7661. *
  7662. * @product highstock gantt
  7663. * @optionparent rangeSelector
  7664. */
  7665. rangeSelector: {
  7666. /**
  7667. * Whether to enable all buttons from the start. By default buttons are
  7668. * only enabled if the corresponding time range exists on the X axis,
  7669. * but enabling all buttons allows for dynamically loading different
  7670. * time ranges.
  7671. *
  7672. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  7673. * All buttons enabled
  7674. *
  7675. * @since 2.0.3
  7676. */
  7677. allButtonsEnabled: false,
  7678. /**
  7679. * An array of configuration objects for the buttons.
  7680. *
  7681. * Defaults to:
  7682. * ```js
  7683. * buttons: [{
  7684. * type: 'month',
  7685. * count: 1,
  7686. * text: '1m',
  7687. * title: 'View 1 month'
  7688. * }, {
  7689. * type: 'month',
  7690. * count: 3,
  7691. * text: '3m',
  7692. * title: 'View 3 months'
  7693. * }, {
  7694. * type: 'month',
  7695. * count: 6,
  7696. * text: '6m',
  7697. * title: 'View 6 months'
  7698. * }, {
  7699. * type: 'ytd',
  7700. * text: 'YTD',
  7701. * title: 'View year to date'
  7702. * }, {
  7703. * type: 'year',
  7704. * count: 1,
  7705. * text: '1y',
  7706. * title: 'View 1 year'
  7707. * }, {
  7708. * type: 'all',
  7709. * text: 'All',
  7710. * title: 'View all'
  7711. * }]
  7712. * ```
  7713. *
  7714. * @sample {highstock} stock/rangeselector/datagrouping/
  7715. * Data grouping by buttons
  7716. *
  7717. * @type {Array<*>}
  7718. */
  7719. buttons: void 0,
  7720. /**
  7721. * How many units of the defined type the button should span. If `type`
  7722. * is "month" and `count` is 3, the button spans three months.
  7723. *
  7724. * @type {number}
  7725. * @default 1
  7726. * @apioption rangeSelector.buttons.count
  7727. */
  7728. /**
  7729. * Fires when clicking on the rangeSelector button. One parameter,
  7730. * event, is passed to the function, containing common event
  7731. * information.
  7732. *
  7733. * ```js
  7734. * click: function(e) {
  7735. * console.log(this);
  7736. * }
  7737. * ```
  7738. *
  7739. * Return false to stop default button's click action.
  7740. *
  7741. * @sample {highstock} stock/rangeselector/button-click/
  7742. * Click event on the button
  7743. *
  7744. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  7745. * @apioption rangeSelector.buttons.events.click
  7746. */
  7747. /**
  7748. * Additional range (in milliseconds) added to the end of the calculated
  7749. * time span.
  7750. *
  7751. * @sample {highstock} stock/rangeselector/min-max-offsets/
  7752. * Button offsets
  7753. *
  7754. * @type {number}
  7755. * @default 0
  7756. * @since 6.0.0
  7757. * @apioption rangeSelector.buttons.offsetMax
  7758. */
  7759. /**
  7760. * Additional range (in milliseconds) added to the start of the
  7761. * calculated time span.
  7762. *
  7763. * @sample {highstock} stock/rangeselector/min-max-offsets/
  7764. * Button offsets
  7765. *
  7766. * @type {number}
  7767. * @default 0
  7768. * @since 6.0.0
  7769. * @apioption rangeSelector.buttons.offsetMin
  7770. */
  7771. /**
  7772. * When buttons apply dataGrouping on a series, by default zooming
  7773. * in/out will deselect buttons and unset dataGrouping. Enable this
  7774. * option to keep buttons selected when extremes change.
  7775. *
  7776. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  7777. * Different preserveDataGrouping settings
  7778. *
  7779. * @type {boolean}
  7780. * @default false
  7781. * @since 6.1.2
  7782. * @apioption rangeSelector.buttons.preserveDataGrouping
  7783. */
  7784. /**
  7785. * A custom data grouping object for each button.
  7786. *
  7787. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  7788. *
  7789. * @sample {highstock} stock/rangeselector/datagrouping/
  7790. * Data grouping by range selector buttons
  7791. *
  7792. * @type {*}
  7793. * @extends plotOptions.series.dataGrouping
  7794. * @apioption rangeSelector.buttons.dataGrouping
  7795. */
  7796. /**
  7797. * The text for the button itself.
  7798. *
  7799. * @type {string}
  7800. * @apioption rangeSelector.buttons.text
  7801. */
  7802. /**
  7803. * Explanation for the button, shown as a tooltip on hover, and used by
  7804. * assistive technology.
  7805. *
  7806. * @type {string}
  7807. * @apioption rangeSelector.buttons.title
  7808. */
  7809. /**
  7810. * Defined the time span for the button. Can be one of `millisecond`,
  7811. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  7812. * and `all`.
  7813. *
  7814. * @type {Highcharts.RangeSelectorButtonTypeValue}
  7815. * @apioption rangeSelector.buttons.type
  7816. */
  7817. /**
  7818. * The space in pixels between the buttons in the range selector.
  7819. */
  7820. buttonSpacing: 5,
  7821. /**
  7822. * Whether to collapse the range selector buttons into a dropdown when
  7823. * there is not enough room to show everything in a single row, instead
  7824. * of dividing the range selector into multiple rows.
  7825. * Can be one of the following:
  7826. * - `always`: Always collapse
  7827. * - `responsive`: Only collapse when there is not enough room
  7828. * - `never`: Never collapse
  7829. *
  7830. * @sample {highstock} stock/rangeselector/dropdown/
  7831. * Dropdown option
  7832. *
  7833. * @validvalue ["always", "responsive", "never"]
  7834. * @since 9.0.0
  7835. */
  7836. dropdown: 'responsive',
  7837. /**
  7838. * Enable or disable the range selector. Default to `true` for stock
  7839. * charts, using the `stockChart` factory.
  7840. *
  7841. * @sample {highstock} stock/rangeselector/enabled/
  7842. * Disable the range selector
  7843. *
  7844. * @type {boolean|undefined}
  7845. * @default {highstock} true
  7846. */
  7847. enabled: void 0,
  7848. /**
  7849. * The vertical alignment of the rangeselector box. Allowed properties
  7850. * are `top`, `middle`, `bottom`.
  7851. *
  7852. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  7853. * Middle
  7854. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  7855. * Bottom
  7856. *
  7857. * @type {Highcharts.VerticalAlignValue}
  7858. * @since 6.0.0
  7859. */
  7860. verticalAlign: 'top',
  7861. /**
  7862. * A collection of attributes for the buttons. The object takes SVG
  7863. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  7864. * a collection of CSS properties for the text.
  7865. *
  7866. * The object can also be extended with states, so you can set
  7867. * presentational options for `hover`, `select` or `disabled` button
  7868. * states.
  7869. *
  7870. * CSS styles for the text label.
  7871. *
  7872. * In styled mode, the buttons are styled by the
  7873. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  7874. * different states.
  7875. *
  7876. * @sample {highstock} stock/rangeselector/styling/
  7877. * Styling the buttons and inputs
  7878. *
  7879. * @type {Highcharts.SVGAttributes}
  7880. */
  7881. buttonTheme: {
  7882. /** @ignore */
  7883. width: 28,
  7884. /** @ignore */
  7885. height: 18,
  7886. /** @ignore */
  7887. padding: 2,
  7888. /** @ignore */
  7889. zIndex: 7 // #484, #852
  7890. },
  7891. /**
  7892. * When the rangeselector is floating, the plot area does not reserve
  7893. * space for it. This opens for positioning anywhere on the chart.
  7894. *
  7895. * @sample {highstock} stock/rangeselector/floating/
  7896. * Placing the range selector between the plot area and the
  7897. * navigator
  7898. *
  7899. * @since 6.0.0
  7900. */
  7901. floating: false,
  7902. /**
  7903. * The x offset of the range selector relative to its horizontal
  7904. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  7905. *
  7906. * @since 6.0.0
  7907. */
  7908. x: 0,
  7909. /**
  7910. * The y offset of the range selector relative to its horizontal
  7911. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  7912. *
  7913. * @since 6.0.0
  7914. */
  7915. y: 0,
  7916. /**
  7917. * Deprecated. The height of the range selector. Currently it is
  7918. * calculated dynamically.
  7919. *
  7920. * @deprecated
  7921. * @type {number|undefined}
  7922. * @since 2.1.9
  7923. */
  7924. height: void 0,
  7925. /**
  7926. * The border color of the date input boxes.
  7927. *
  7928. * @sample {highstock} stock/rangeselector/styling/
  7929. * Styling the buttons and inputs
  7930. *
  7931. * @type {Highcharts.ColorString}
  7932. * @since 1.3.7
  7933. */
  7934. inputBoxBorderColor: 'none',
  7935. /**
  7936. * The pixel height of the date input boxes.
  7937. *
  7938. * @sample {highstock} stock/rangeselector/styling/
  7939. * Styling the buttons and inputs
  7940. *
  7941. * @since 1.3.7
  7942. */
  7943. inputBoxHeight: 17,
  7944. /**
  7945. * The pixel width of the date input boxes. When `undefined`, the width
  7946. * is fitted to the rendered content.
  7947. *
  7948. * @sample {highstock} stock/rangeselector/styling/
  7949. * Styling the buttons and inputs
  7950. *
  7951. * @type {number|undefined}
  7952. * @since 1.3.7
  7953. */
  7954. inputBoxWidth: void 0,
  7955. /**
  7956. * The date format in the input boxes when not selected for editing.
  7957. * Defaults to `%b %e, %Y`.
  7958. *
  7959. * This is used to determine which type of input to show,
  7960. * `datetime-local`, `date` or `time` and falling back to `text` when
  7961. * the browser does not support the input type or the format contains
  7962. * milliseconds.
  7963. *
  7964. * @sample {highstock} stock/rangeselector/input-type/
  7965. * Input types
  7966. * @sample {highstock} stock/rangeselector/input-format/
  7967. * Milliseconds in the range selector
  7968. *
  7969. */
  7970. inputDateFormat: '%b %e, %Y',
  7971. /**
  7972. * A custom callback function to parse values entered in the input boxes
  7973. * and return a valid JavaScript time as milliseconds since 1970.
  7974. * The first argument passed is a value to parse,
  7975. * second is a boolean indicating use of the UTC time.
  7976. *
  7977. * This will only get called for inputs of type `text`. Since v8.2.3,
  7978. * the input type is dynamically determined based on the granularity
  7979. * of the `inputDateFormat` and the browser support.
  7980. *
  7981. * @sample {highstock} stock/rangeselector/input-format/
  7982. * Milliseconds in the range selector
  7983. *
  7984. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  7985. * @since 1.3.3
  7986. */
  7987. inputDateParser: void 0,
  7988. /**
  7989. * The date format in the input boxes when they are selected for
  7990. * editing. This must be a format that is recognized by JavaScript
  7991. * Date.parse.
  7992. *
  7993. * This will only be used for inputs of type `text`. Since v8.2.3,
  7994. * the input type is dynamically determined based on the granularity
  7995. * of the `inputDateFormat` and the browser support.
  7996. *
  7997. * @sample {highstock} stock/rangeselector/input-format/
  7998. * Milliseconds in the range selector
  7999. *
  8000. */
  8001. inputEditDateFormat: '%Y-%m-%d',
  8002. /**
  8003. * Enable or disable the date input boxes.
  8004. */
  8005. inputEnabled: true,
  8006. /**
  8007. * Positioning for the input boxes. Allowed properties are `align`,
  8008. * `x` and `y`.
  8009. *
  8010. * @since 1.2.4
  8011. */
  8012. inputPosition: {
  8013. /**
  8014. * The alignment of the input box. Allowed properties are `left`,
  8015. * `center`, `right`.
  8016. *
  8017. * @sample {highstock} stock/rangeselector/input-button-position/
  8018. * Alignment
  8019. *
  8020. * @type {Highcharts.AlignValue}
  8021. * @since 6.0.0
  8022. */
  8023. align: 'right',
  8024. /**
  8025. * X offset of the input row.
  8026. */
  8027. x: 0,
  8028. /**
  8029. * Y offset of the input row.
  8030. */
  8031. y: 0
  8032. },
  8033. /**
  8034. * The space in pixels between the labels and the date input boxes in
  8035. * the range selector.
  8036. *
  8037. * @since 9.0.0
  8038. */
  8039. inputSpacing: 5,
  8040. /**
  8041. * The index of the button to appear pre-selected.
  8042. *
  8043. * @type {number}
  8044. */
  8045. selected: void 0,
  8046. /**
  8047. * Positioning for the button row.
  8048. *
  8049. * @since 1.2.4
  8050. */
  8051. buttonPosition: {
  8052. /**
  8053. * The alignment of the input box. Allowed properties are `left`,
  8054. * `center`, `right`.
  8055. *
  8056. * @sample {highstock} stock/rangeselector/input-button-position/
  8057. * Alignment
  8058. *
  8059. * @type {Highcharts.AlignValue}
  8060. * @since 6.0.0
  8061. */
  8062. align: 'left',
  8063. /**
  8064. * X offset of the button row.
  8065. */
  8066. x: 0,
  8067. /**
  8068. * Y offset of the button row.
  8069. */
  8070. y: 0
  8071. },
  8072. /**
  8073. * CSS for the HTML inputs in the range selector.
  8074. *
  8075. * In styled mode, the inputs are styled by the
  8076. * `.highcharts-range-input text` rule in SVG mode, and
  8077. * `input.highcharts-range-selector` when active.
  8078. *
  8079. * @sample {highstock} stock/rangeselector/styling/
  8080. * Styling the buttons and inputs
  8081. *
  8082. * @type {Highcharts.CSSObject}
  8083. * @apioption rangeSelector.inputStyle
  8084. */
  8085. inputStyle: {
  8086. /** @ignore */
  8087. color: palette.highlightColor80,
  8088. /** @ignore */
  8089. cursor: 'pointer'
  8090. },
  8091. /**
  8092. * CSS styles for the labels - the Zoom, From and To texts.
  8093. *
  8094. * In styled mode, the labels are styled by the
  8095. * `.highcharts-range-label` class.
  8096. *
  8097. * @sample {highstock} stock/rangeselector/styling/
  8098. * Styling the buttons and inputs
  8099. *
  8100. * @type {Highcharts.CSSObject}
  8101. */
  8102. labelStyle: {
  8103. /** @ignore */
  8104. color: palette.neutralColor60
  8105. }
  8106. }
  8107. });
  8108. extend(defaultOptions.lang,
  8109. /**
  8110. * Language object. The language object is global and it can't be set
  8111. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  8112. * set it before any chart is initialized.
  8113. *
  8114. * ```js
  8115. * Highcharts.setOptions({
  8116. * lang: {
  8117. * months: [
  8118. * 'Janvier', 'Février', 'Mars', 'Avril',
  8119. * 'Mai', 'Juin', 'Juillet', 'Août',
  8120. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  8121. * ],
  8122. * weekdays: [
  8123. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  8124. * 'Jeudi', 'Vendredi', 'Samedi'
  8125. * ]
  8126. * }
  8127. * });
  8128. * ```
  8129. *
  8130. * @optionparent lang
  8131. */
  8132. {
  8133. /**
  8134. * The text for the label for the range selector buttons.
  8135. *
  8136. * @product highstock gantt
  8137. */
  8138. rangeSelectorZoom: 'Zoom',
  8139. /**
  8140. * The text for the label for the "from" input box in the range
  8141. * selector. Since v9.0, this string is empty as the label is not
  8142. * rendered by default.
  8143. *
  8144. * @product highstock gantt
  8145. */
  8146. rangeSelectorFrom: '',
  8147. /**
  8148. * The text for the label for the "to" input box in the range selector.
  8149. *
  8150. * @product highstock gantt
  8151. */
  8152. rangeSelectorTo: '→'
  8153. });
  8154. /* eslint-disable no-invalid-this, valid-jsdoc */
  8155. /**
  8156. * The range selector.
  8157. *
  8158. * @private
  8159. * @class
  8160. * @name Highcharts.RangeSelector
  8161. * @param {Highcharts.Chart} chart
  8162. */
  8163. var RangeSelector = /** @class */ (function () {
  8164. function RangeSelector(chart) {
  8165. /* *
  8166. *
  8167. * Properties
  8168. *
  8169. * */
  8170. this.buttons = void 0;
  8171. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  8172. this.initialButtonGroupWidth = 0;
  8173. this.options = void 0;
  8174. this.chart = chart;
  8175. // Run RangeSelector
  8176. this.init(chart);
  8177. }
  8178. /**
  8179. * The method to run when one of the buttons in the range selectors is
  8180. * clicked
  8181. *
  8182. * @private
  8183. * @function Highcharts.RangeSelector#clickButton
  8184. * @param {number} i
  8185. * The index of the button
  8186. * @param {boolean} [redraw]
  8187. * @return {void}
  8188. */
  8189. RangeSelector.prototype.clickButton = function (i, redraw) {
  8190. var rangeSelector = this,
  8191. chart = rangeSelector.chart,
  8192. rangeOptions = rangeSelector.buttonOptions[i],
  8193. baseAxis = chart.xAxis[0],
  8194. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  8195. dataMin = unionExtremes.dataMin,
  8196. dataMax = unionExtremes.dataMax,
  8197. newMin,
  8198. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  8199. pick(dataMax,
  8200. baseAxis.max))), // #1568
  8201. type = rangeOptions.type,
  8202. baseXAxisOptions,
  8203. range = rangeOptions._range,
  8204. rangeMin,
  8205. minSetting,
  8206. rangeSetting,
  8207. ctx,
  8208. ytdExtremes,
  8209. dataGrouping = rangeOptions.dataGrouping;
  8210. // chart has no data, base series is removed
  8211. if (dataMin === null || dataMax === null) {
  8212. return;
  8213. }
  8214. // Set the fixed range before range is altered
  8215. chart.fixedRange = range;
  8216. // Apply dataGrouping associated to button
  8217. if (dataGrouping) {
  8218. this.forcedDataGrouping = true;
  8219. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  8220. this.frozenStates = rangeOptions.preserveDataGrouping;
  8221. }
  8222. // Apply range
  8223. if (type === 'month' || type === 'year') {
  8224. if (!baseAxis) {
  8225. // This is set to the user options and picked up later when the
  8226. // axis is instantiated so that we know the min and max.
  8227. range = rangeOptions;
  8228. }
  8229. else {
  8230. ctx = {
  8231. range: rangeOptions,
  8232. max: newMax,
  8233. chart: chart,
  8234. dataMin: dataMin,
  8235. dataMax: dataMax
  8236. };
  8237. newMin = baseAxis.minFromRange.call(ctx);
  8238. if (isNumber(ctx.newMax)) {
  8239. newMax = ctx.newMax;
  8240. }
  8241. }
  8242. // Fixed times like minutes, hours, days
  8243. }
  8244. else if (range) {
  8245. newMin = Math.max(newMax - range, dataMin);
  8246. newMax = Math.min(newMin + range, dataMax);
  8247. }
  8248. else if (type === 'ytd') {
  8249. // On user clicks on the buttons, or a delayed action running from
  8250. // the beforeRender event (below), the baseAxis is defined.
  8251. if (baseAxis) {
  8252. // When "ytd" is the pre-selected button for the initial view,
  8253. // its calculation is delayed and rerun in the beforeRender
  8254. // event (below). When the series are initialized, but before
  8255. // the chart is rendered, we have access to the xData array
  8256. // (#942).
  8257. if (typeof dataMax === 'undefined') {
  8258. dataMin = Number.MAX_VALUE;
  8259. dataMax = Number.MIN_VALUE;
  8260. chart.series.forEach(function (series) {
  8261. // reassign it to the last item
  8262. var xData = series.xData;
  8263. dataMin = Math.min(xData[0], dataMin);
  8264. dataMax = Math.max(xData[xData.length - 1], dataMax);
  8265. });
  8266. redraw = false;
  8267. }
  8268. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  8269. newMin = rangeMin = ytdExtremes.min;
  8270. newMax = ytdExtremes.max;
  8271. // "ytd" is pre-selected. We don't yet have access to processed
  8272. // point and extremes data (things like pointStart and pointInterval
  8273. // are missing), so we delay the process (#942)
  8274. }
  8275. else {
  8276. rangeSelector.deferredYTDClick = i;
  8277. return;
  8278. }
  8279. }
  8280. else if (type === 'all' && baseAxis) {
  8281. newMin = dataMin;
  8282. newMax = dataMax;
  8283. }
  8284. if (defined(newMin)) {
  8285. newMin += rangeOptions._offsetMin;
  8286. }
  8287. if (defined(newMax)) {
  8288. newMax += rangeOptions._offsetMax;
  8289. }
  8290. rangeSelector.setSelected(i);
  8291. if (this.dropdown) {
  8292. this.dropdown.selectedIndex = i + 1;
  8293. }
  8294. // Update the chart
  8295. if (!baseAxis) {
  8296. // Axis not yet instanciated. Temporarily set min and range
  8297. // options and remove them on chart load (#4317).
  8298. baseXAxisOptions = splat(chart.options.xAxis)[0];
  8299. rangeSetting = baseXAxisOptions.range;
  8300. baseXAxisOptions.range = range;
  8301. minSetting = baseXAxisOptions.min;
  8302. baseXAxisOptions.min = rangeMin;
  8303. addEvent(chart, 'load', function resetMinAndRange() {
  8304. baseXAxisOptions.range = rangeSetting;
  8305. baseXAxisOptions.min = minSetting;
  8306. });
  8307. }
  8308. else {
  8309. // Existing axis object. Set extremes after render time.
  8310. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  8311. {
  8312. trigger: 'rangeSelectorButton',
  8313. rangeSelectorButton: rangeOptions
  8314. });
  8315. }
  8316. fireEvent(this, 'afterBtnClick');
  8317. };
  8318. /**
  8319. * Set the selected option. This method only sets the internal flag, it
  8320. * doesn't update the buttons or the actual zoomed range.
  8321. *
  8322. * @private
  8323. * @function Highcharts.RangeSelector#setSelected
  8324. * @param {number} [selected]
  8325. * @return {void}
  8326. */
  8327. RangeSelector.prototype.setSelected = function (selected) {
  8328. this.selected = this.options.selected = selected;
  8329. };
  8330. /**
  8331. * Initialize the range selector
  8332. *
  8333. * @private
  8334. * @function Highcharts.RangeSelector#init
  8335. * @param {Highcharts.Chart} chart
  8336. * @return {void}
  8337. */
  8338. RangeSelector.prototype.init = function (chart) {
  8339. var rangeSelector = this,
  8340. options = chart.options.rangeSelector,
  8341. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  8342. selectedOption = options.selected,
  8343. blurInputs = function () {
  8344. var minInput = rangeSelector.minInput,
  8345. maxInput = rangeSelector.maxInput;
  8346. // #3274 in some case blur is not defined
  8347. if (minInput && minInput.blur) {
  8348. fireEvent(minInput, 'blur');
  8349. }
  8350. if (maxInput && maxInput.blur) {
  8351. fireEvent(maxInput, 'blur');
  8352. }
  8353. };
  8354. rangeSelector.chart = chart;
  8355. rangeSelector.options = options;
  8356. rangeSelector.buttons = [];
  8357. rangeSelector.buttonOptions = buttonOptions;
  8358. this.eventsToUnbind = [];
  8359. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  8360. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  8361. // Extend the buttonOptions with actual range
  8362. buttonOptions.forEach(rangeSelector.computeButtonRange);
  8363. // zoomed range based on a pre-selected button index
  8364. if (typeof selectedOption !== 'undefined' &&
  8365. buttonOptions[selectedOption]) {
  8366. this.clickButton(selectedOption, false);
  8367. }
  8368. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  8369. // If a data grouping is applied to the current button, release it
  8370. // when extremes change
  8371. if (chart.xAxis && chart.xAxis[0]) {
  8372. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  8373. if (this.max - this.min !==
  8374. chart.fixedRange &&
  8375. e.trigger !== 'rangeSelectorButton' &&
  8376. e.trigger !== 'updatedData' &&
  8377. rangeSelector.forcedDataGrouping &&
  8378. !rangeSelector.frozenStates) {
  8379. this.setDataGrouping(false, false);
  8380. }
  8381. });
  8382. }
  8383. }));
  8384. };
  8385. /**
  8386. * Dynamically update the range selector buttons after a new range has been
  8387. * set
  8388. *
  8389. * @private
  8390. * @function Highcharts.RangeSelector#updateButtonStates
  8391. * @return {void}
  8392. */
  8393. RangeSelector.prototype.updateButtonStates = function () {
  8394. var rangeSelector = this,
  8395. chart = this.chart,
  8396. dropdown = this.dropdown,
  8397. baseAxis = chart.xAxis[0],
  8398. actualRange = Math.round(baseAxis.max - baseAxis.min),
  8399. hasNoData = !baseAxis.hasVisibleSeries,
  8400. day = 24 * 36e5, // A single day in milliseconds
  8401. unionExtremes = (chart.scroller &&
  8402. chart.scroller.getUnionExtremes()) || baseAxis,
  8403. dataMin = unionExtremes.dataMin,
  8404. dataMax = unionExtremes.dataMax,
  8405. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  8406. dataMin,
  8407. chart.time.useUTC),
  8408. ytdMin = ytdExtremes.min,
  8409. ytdMax = ytdExtremes.max,
  8410. selected = rangeSelector.selected,
  8411. selectedExists = isNumber(selected),
  8412. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  8413. buttons = rangeSelector.buttons;
  8414. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  8415. var range = rangeOptions._range,
  8416. type = rangeOptions.type,
  8417. count = rangeOptions.count || 1,
  8418. button = buttons[i],
  8419. state = 0,
  8420. disable,
  8421. select,
  8422. offsetRange = rangeOptions._offsetMax -
  8423. rangeOptions._offsetMin,
  8424. isSelected = i === selected,
  8425. // Disable buttons where the range exceeds what is allowed in
  8426. // the current view
  8427. isTooGreatRange = range >
  8428. dataMax - dataMin,
  8429. // Disable buttons where the range is smaller than the minimum
  8430. // range
  8431. isTooSmallRange = range < baseAxis.minRange,
  8432. // Do not select the YTD button if not explicitly told so
  8433. isYTDButNotSelected = false,
  8434. // Disable the All button if we're already showing all
  8435. isAllButAlreadyShowingAll = false,
  8436. isSameRange = range === actualRange;
  8437. // Months and years have a variable range so we check the extremes
  8438. if ((type === 'month' || type === 'year') &&
  8439. (actualRange + 36e5 >=
  8440. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  8441. (actualRange - 36e5 <=
  8442. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  8443. isSameRange = true;
  8444. }
  8445. else if (type === 'ytd') {
  8446. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  8447. isYTDButNotSelected = !isSelected;
  8448. }
  8449. else if (type === 'all') {
  8450. isSameRange = (baseAxis.max - baseAxis.min >=
  8451. dataMax - dataMin);
  8452. isAllButAlreadyShowingAll = (!isSelected &&
  8453. selectedExists &&
  8454. isSameRange);
  8455. }
  8456. // The new zoom area happens to match the range for a button - mark
  8457. // it selected. This happens when scrolling across an ordinal gap.
  8458. // It can be seen in the intraday demos when selecting 1h and scroll
  8459. // across the night gap.
  8460. disable = (!allButtonsEnabled &&
  8461. (isTooGreatRange ||
  8462. isTooSmallRange ||
  8463. isAllButAlreadyShowingAll ||
  8464. hasNoData));
  8465. select = ((isSelected && isSameRange) ||
  8466. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  8467. (isSelected && rangeSelector.frozenStates));
  8468. if (disable) {
  8469. state = 3;
  8470. }
  8471. else if (select) {
  8472. selectedExists = true; // Only one button can be selected
  8473. state = 2;
  8474. }
  8475. // If state has changed, update the button
  8476. if (button.state !== state) {
  8477. button.setState(state);
  8478. if (dropdown) {
  8479. dropdown.options[i + 1].disabled = disable;
  8480. if (state === 2) {
  8481. dropdown.selectedIndex = i + 1;
  8482. }
  8483. }
  8484. // Reset (#9209)
  8485. if (state === 0 && selected === i) {
  8486. rangeSelector.setSelected();
  8487. }
  8488. }
  8489. });
  8490. };
  8491. /**
  8492. * Compute and cache the range for an individual button
  8493. *
  8494. * @private
  8495. * @function Highcharts.RangeSelector#computeButtonRange
  8496. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  8497. * @return {void}
  8498. */
  8499. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  8500. var type = rangeOptions.type,
  8501. count = rangeOptions.count || 1,
  8502. // these time intervals have a fixed number of milliseconds, as
  8503. // opposed to month, ytd and year
  8504. fixedTimes = {
  8505. millisecond: 1,
  8506. second: 1000,
  8507. minute: 60 * 1000,
  8508. hour: 3600 * 1000,
  8509. day: 24 * 3600 * 1000,
  8510. week: 7 * 24 * 3600 * 1000
  8511. };
  8512. // Store the range on the button object
  8513. if (fixedTimes[type]) {
  8514. rangeOptions._range = fixedTimes[type] * count;
  8515. }
  8516. else if (type === 'month' || type === 'year') {
  8517. rangeOptions._range = {
  8518. month: 30,
  8519. year: 365
  8520. }[type] * 24 * 36e5 * count;
  8521. }
  8522. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  8523. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  8524. rangeOptions._range +=
  8525. rangeOptions._offsetMax - rangeOptions._offsetMin;
  8526. };
  8527. /**
  8528. * Get the unix timestamp of a HTML input for the dates
  8529. *
  8530. * @private
  8531. * @function Highcharts.RangeSelector#getInputValue
  8532. * @param {string} name
  8533. * @return {number}
  8534. */
  8535. RangeSelector.prototype.getInputValue = function (name) {
  8536. var input = name === 'min' ? this.minInput : this.maxInput;
  8537. var options = this.chart.options.rangeSelector;
  8538. var time = this.chart.time;
  8539. if (input) {
  8540. return ((input.type === 'text' && options.inputDateParser) ||
  8541. this.defaultInputDateParser)(input.value, time.useUTC, time);
  8542. }
  8543. return 0;
  8544. };
  8545. /**
  8546. * Set the internal and displayed value of a HTML input for the dates
  8547. *
  8548. * @private
  8549. * @function Highcharts.RangeSelector#setInputValue
  8550. * @param {string} name
  8551. * @param {number} [inputTime]
  8552. * @return {void}
  8553. */
  8554. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  8555. var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8556. if (input) {
  8557. var hcTimeAttr = input.getAttribute('data-hc-time');
  8558. var updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  8559. if (defined(inputTime)) {
  8560. var previousTime = updatedTime;
  8561. if (defined(previousTime)) {
  8562. input.setAttribute('data-hc-time-previous', previousTime);
  8563. }
  8564. input.setAttribute('data-hc-time', inputTime);
  8565. updatedTime = inputTime;
  8566. }
  8567. input.value = time.dateFormat(this.inputTypeFormats[input.type] || options.inputEditDateFormat, updatedTime);
  8568. if (dateBox) {
  8569. dateBox.attr({
  8570. text: time.dateFormat(options.inputDateFormat, updatedTime)
  8571. });
  8572. }
  8573. }
  8574. };
  8575. /**
  8576. * Set the min and max value of a HTML input for the dates
  8577. *
  8578. * @private
  8579. * @function Highcharts.RangeSelector#setInputExtremes
  8580. * @param {string} name
  8581. * @param {number} min
  8582. * @param {number} max
  8583. * @return {void}
  8584. */
  8585. RangeSelector.prototype.setInputExtremes = function (name, min, max) {
  8586. var input = name === 'min' ? this.minInput : this.maxInput;
  8587. if (input) {
  8588. var format = this.inputTypeFormats[input.type];
  8589. var time = this.chart.time;
  8590. if (format) {
  8591. var newMin = time.dateFormat(format,
  8592. min);
  8593. if (input.min !== newMin) {
  8594. input.min = newMin;
  8595. }
  8596. var newMax = time.dateFormat(format,
  8597. max);
  8598. if (input.max !== newMax) {
  8599. input.max = newMax;
  8600. }
  8601. }
  8602. }
  8603. };
  8604. /**
  8605. * @private
  8606. * @function Highcharts.RangeSelector#showInput
  8607. * @param {string} name
  8608. * @return {void}
  8609. */
  8610. RangeSelector.prototype.showInput = function (name) {
  8611. var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8612. var input = name === 'min' ? this.minInput : this.maxInput;
  8613. if (input && dateBox && this.inputGroup) {
  8614. var isTextInput = input.type === 'text';
  8615. var _a = this.inputGroup,
  8616. translateX = _a.translateX,
  8617. translateY = _a.translateY;
  8618. css(input, {
  8619. width: isTextInput ? ((dateBox.width - 2) + 'px') : 'auto',
  8620. height: isTextInput ? ((dateBox.height - 2) + 'px') : 'auto',
  8621. border: '2px solid silver'
  8622. });
  8623. if (isTextInput) {
  8624. css(input, {
  8625. left: (translateX + dateBox.x) + 'px',
  8626. top: translateY + 'px'
  8627. });
  8628. // Inputs of types date, time or datetime-local should be centered
  8629. // on top of the dateBox
  8630. }
  8631. else {
  8632. css(input, {
  8633. left: Math.min(Math.round(dateBox.x +
  8634. translateX -
  8635. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  8636. top: (translateY - (input.offsetHeight - dateBox.height) / 2) + 'px'
  8637. });
  8638. }
  8639. }
  8640. };
  8641. /**
  8642. * @private
  8643. * @function Highcharts.RangeSelector#hideInput
  8644. * @param {string} name
  8645. * @return {void}
  8646. */
  8647. RangeSelector.prototype.hideInput = function (name) {
  8648. var input = name === 'min' ? this.minInput : this.maxInput;
  8649. if (input) {
  8650. css(input, {
  8651. top: '-9999em',
  8652. border: 0,
  8653. width: '1px',
  8654. height: '1px'
  8655. });
  8656. }
  8657. };
  8658. /**
  8659. * @private
  8660. * @function Highcharts.RangeSelector#defaultInputDateParser
  8661. */
  8662. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
  8663. var hasTimezone = function (str) {
  8664. return str.length > 6 &&
  8665. (str.lastIndexOf('-') === str.length - 6 ||
  8666. str.lastIndexOf('+') === str.length - 6);
  8667. };
  8668. var input = inputDate.split('/').join('-').split(' ').join('T');
  8669. if (input.indexOf('T') === -1) {
  8670. input += 'T00:00';
  8671. }
  8672. if (useUTC) {
  8673. input += 'Z';
  8674. }
  8675. else if (H.isSafari && !hasTimezone(input)) {
  8676. var offset = new Date(input).getTimezoneOffset() / 60;
  8677. input += offset <= 0 ? "+" + pad(-offset) + ":00" : "-" + pad(offset) + ":00";
  8678. }
  8679. var date = Date.parse(input);
  8680. // If the value isn't parsed directly to a value by the
  8681. // browser's Date.parse method, like YYYY-MM-DD in IE8, try
  8682. // parsing it a different way
  8683. if (!isNumber(date)) {
  8684. var parts = inputDate.split('-');
  8685. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  8686. }
  8687. if (time && useUTC) {
  8688. date += time.getTimezoneOffset(date);
  8689. }
  8690. return date;
  8691. };
  8692. /**
  8693. * Draw either the 'from' or the 'to' HTML input box of the range selector
  8694. *
  8695. * @private
  8696. * @function Highcharts.RangeSelector#drawInput
  8697. * @param {string} name
  8698. * @return {RangeSelectorInputElements}
  8699. */
  8700. RangeSelector.prototype.drawInput = function (name) {
  8701. var _a = this,
  8702. chart = _a.chart,
  8703. div = _a.div,
  8704. inputGroup = _a.inputGroup;
  8705. var rangeSelector = this,
  8706. chartStyle = chart.renderer.style || {},
  8707. renderer = chart.renderer,
  8708. options = chart.options.rangeSelector,
  8709. lang = defaultOptions.lang,
  8710. isMin = name === 'min';
  8711. /**
  8712. * @private
  8713. */
  8714. function updateExtremes() {
  8715. var value = rangeSelector.getInputValue(name),
  8716. chartAxis = chart.xAxis[0],
  8717. dataAxis = chart.scroller && chart.scroller.xAxis ?
  8718. chart.scroller.xAxis :
  8719. chartAxis,
  8720. dataMin = dataAxis.dataMin,
  8721. dataMax = dataAxis.dataMax;
  8722. var maxInput = rangeSelector.maxInput,
  8723. minInput = rangeSelector.minInput;
  8724. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  8725. isNumber(value)) {
  8726. input.setAttribute('data-hc-time-previous', value);
  8727. // Validate the extremes. If it goes beyound the data min or
  8728. // max, use the actual data extreme (#2438).
  8729. if (isMin && maxInput && isNumber(dataMin)) {
  8730. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  8731. value = void 0;
  8732. }
  8733. else if (value < dataMin) {
  8734. value = dataMin;
  8735. }
  8736. }
  8737. else if (minInput && isNumber(dataMax)) {
  8738. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  8739. value = void 0;
  8740. }
  8741. else if (value > dataMax) {
  8742. value = dataMax;
  8743. }
  8744. }
  8745. // Set the extremes
  8746. if (typeof value !== 'undefined') { // @todo typof undefined
  8747. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  8748. }
  8749. }
  8750. }
  8751. // Create the text label
  8752. var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'];
  8753. var label = renderer
  8754. .label(text, 0)
  8755. .addClass('highcharts-range-label')
  8756. .attr({
  8757. padding: text ? 2 : 0
  8758. })
  8759. .add(inputGroup);
  8760. // Create an SVG label that shows updated date ranges and and records
  8761. // click events that bring in the HTML input.
  8762. var dateBox = renderer
  8763. .label('', 0)
  8764. .addClass('highcharts-range-input')
  8765. .attr({
  8766. padding: 2,
  8767. width: options.inputBoxWidth,
  8768. height: options.inputBoxHeight,
  8769. 'text-align': 'center'
  8770. })
  8771. .on('click',
  8772. function () {
  8773. // If it is already focused, the onfocus event doesn't fire
  8774. // (#3713)
  8775. rangeSelector.showInput(name);
  8776. rangeSelector[name + 'Input'].focus();
  8777. });
  8778. if (!chart.styledMode) {
  8779. dateBox.attr({
  8780. stroke: options.inputBoxBorderColor,
  8781. 'stroke-width': 1
  8782. });
  8783. }
  8784. dateBox.add(inputGroup);
  8785. // Create the HTML input element. This is rendered as 1x1 pixel then set
  8786. // to the right size when focused.
  8787. var input = createElement('input', {
  8788. name: name,
  8789. className: 'highcharts-range-selector'
  8790. },
  8791. void 0,
  8792. div);
  8793. // #14788: Setting input.type to an unsupported type throws in IE, so
  8794. // we need to use setAttribute instead
  8795. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%b %e, %Y'));
  8796. if (!chart.styledMode) {
  8797. // Styles
  8798. label.css(merge(chartStyle, options.labelStyle));
  8799. dateBox.css(merge({
  8800. color: palette.neutralColor80
  8801. }, chartStyle, options.inputStyle));
  8802. css(input, extend({
  8803. position: 'absolute',
  8804. border: 0,
  8805. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  8806. width: '1px',
  8807. height: '1px',
  8808. padding: 0,
  8809. textAlign: 'center',
  8810. fontSize: chartStyle.fontSize,
  8811. fontFamily: chartStyle.fontFamily,
  8812. top: '-9999em' // #4798
  8813. }, options.inputStyle));
  8814. }
  8815. // Blow up the input box
  8816. input.onfocus = function () {
  8817. rangeSelector.showInput(name);
  8818. };
  8819. // Hide away the input box
  8820. input.onblur = function () {
  8821. // update extermes only when inputs are active
  8822. if (input === H.doc.activeElement) { // Only when focused
  8823. // Update also when no `change` event is triggered, like when
  8824. // clicking inside the SVG (#4710)
  8825. updateExtremes();
  8826. }
  8827. // #10404 - move hide and blur outside focus
  8828. rangeSelector.hideInput(name);
  8829. rangeSelector.setInputValue(name);
  8830. input.blur(); // #4606
  8831. };
  8832. var keyDown = false;
  8833. // handle changes in the input boxes
  8834. input.onchange = function () {
  8835. updateExtremes();
  8836. // Blur input when clicking date input calendar
  8837. if (!keyDown) {
  8838. rangeSelector.hideInput(name);
  8839. input.blur();
  8840. }
  8841. };
  8842. input.onkeypress = function (event) {
  8843. // IE does not fire onchange on enter
  8844. if (event.keyCode === 13) {
  8845. updateExtremes();
  8846. }
  8847. };
  8848. input.onkeydown = function () {
  8849. keyDown = true;
  8850. };
  8851. input.onkeyup = function () {
  8852. keyDown = false;
  8853. };
  8854. return { dateBox: dateBox, input: input, label: label };
  8855. };
  8856. /**
  8857. * Get the position of the range selector buttons and inputs. This can be
  8858. * overridden from outside for custom positioning.
  8859. *
  8860. * @private
  8861. * @function Highcharts.RangeSelector#getPosition
  8862. *
  8863. * @return {Highcharts.Dictionary<number>}
  8864. */
  8865. RangeSelector.prototype.getPosition = function () {
  8866. var chart = this.chart,
  8867. options = chart.options.rangeSelector,
  8868. top = options.verticalAlign === 'top' ?
  8869. chart.plotTop - chart.axisOffset[0] :
  8870. 0; // set offset only for varticalAlign top
  8871. return {
  8872. buttonTop: top + options.buttonPosition.y,
  8873. inputTop: top + options.inputPosition.y - 10
  8874. };
  8875. };
  8876. /**
  8877. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  8878. * the current timestamp. Will choose dataMin if its value is higher than
  8879. * the timestamp for the start of current year.
  8880. *
  8881. * @private
  8882. * @function Highcharts.RangeSelector#getYTDExtremes
  8883. *
  8884. * @param {number} dataMax
  8885. *
  8886. * @param {number} dataMin
  8887. *
  8888. * @return {*}
  8889. * Returns min and max for the YTD
  8890. */
  8891. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  8892. var time = this.chart.time,
  8893. min,
  8894. now = new time.Date(dataMax),
  8895. year = time.get('FullYear',
  8896. now),
  8897. startOfYear = useUTC ?
  8898. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  8899. +new time.Date(year, 0, 1);
  8900. min = Math.max(dataMin, startOfYear);
  8901. var ts = now.getTime();
  8902. return {
  8903. max: Math.min(dataMax || ts, ts),
  8904. min: min
  8905. };
  8906. };
  8907. /**
  8908. * Render the range selector including the buttons and the inputs. The first
  8909. * time render is called, the elements are created and positioned. On
  8910. * subsequent calls, they are moved and updated.
  8911. *
  8912. * @private
  8913. * @function Highcharts.RangeSelector#render
  8914. * @param {number} [min]
  8915. * X axis minimum
  8916. * @param {number} [max]
  8917. * X axis maximum
  8918. * @return {void}
  8919. */
  8920. RangeSelector.prototype.render = function (min, max) {
  8921. var chart = this.chart,
  8922. renderer = chart.renderer,
  8923. container = chart.container,
  8924. chartOptions = chart.options,
  8925. options = chartOptions.rangeSelector,
  8926. // Place inputs above the container
  8927. inputsZIndex = pick(chartOptions.chart.style &&
  8928. chartOptions.chart.style.zIndex, 0) + 1,
  8929. inputEnabled = options.inputEnabled,
  8930. rendered = this.rendered;
  8931. if (options.enabled === false) {
  8932. return;
  8933. }
  8934. // create the elements
  8935. if (!rendered) {
  8936. this.group = renderer.g('range-selector-group')
  8937. .attr({
  8938. zIndex: 7
  8939. })
  8940. .add();
  8941. this.div = createElement('div', void 0, {
  8942. position: 'relative',
  8943. height: 0,
  8944. zIndex: inputsZIndex
  8945. });
  8946. if (this.buttonOptions.length) {
  8947. this.renderButtons();
  8948. }
  8949. // First create a wrapper outside the container in order to make
  8950. // the inputs work and make export correct
  8951. if (container.parentNode) {
  8952. container.parentNode.insertBefore(this.div, container);
  8953. }
  8954. if (inputEnabled) {
  8955. // Create the group to keep the inputs
  8956. this.inputGroup = renderer.g('input-group').add(this.group);
  8957. var minElems = this.drawInput('min');
  8958. this.minDateBox = minElems.dateBox;
  8959. this.minLabel = minElems.label;
  8960. this.minInput = minElems.input;
  8961. var maxElems = this.drawInput('max');
  8962. this.maxDateBox = maxElems.dateBox;
  8963. this.maxLabel = maxElems.label;
  8964. this.maxInput = maxElems.input;
  8965. }
  8966. }
  8967. if (inputEnabled) {
  8968. // Set or reset the input values
  8969. this.setInputValue('min', min);
  8970. this.setInputValue('max', max);
  8971. var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  8972. if (defined(unionExtremes.dataMin) && defined(unionExtremes.dataMax)) {
  8973. var minRange = chart.xAxis[0].minRange || 0;
  8974. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  8975. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  8976. }
  8977. // Reflow
  8978. if (this.inputGroup) {
  8979. var x_1 = 0;
  8980. [
  8981. this.minLabel,
  8982. this.minDateBox,
  8983. this.maxLabel,
  8984. this.maxDateBox
  8985. ].forEach(function (label) {
  8986. if (label && label.width) {
  8987. label.attr({ x: x_1 });
  8988. x_1 += label.width + options.inputSpacing;
  8989. }
  8990. });
  8991. }
  8992. }
  8993. this.alignElements();
  8994. this.rendered = true;
  8995. };
  8996. /**
  8997. * Render the range buttons. This only runs the first time, later the
  8998. * positioning is laid out in alignElements.
  8999. *
  9000. * @private
  9001. * @function Highcharts.RangeSelector#renderButtons
  9002. * @return {void}
  9003. */
  9004. RangeSelector.prototype.renderButtons = function () {
  9005. var _this = this;
  9006. var _a = this,
  9007. buttons = _a.buttons,
  9008. chart = _a.chart,
  9009. options = _a.options;
  9010. var lang = defaultOptions.lang;
  9011. var renderer = chart.renderer;
  9012. var buttonTheme = merge(options.buttonTheme);
  9013. var states = buttonTheme && buttonTheme.states;
  9014. // Prevent the button from resetting the width when the button state
  9015. // changes since we need more control over the width when collapsing
  9016. // the buttons
  9017. var width = buttonTheme.width || 28;
  9018. delete buttonTheme.width;
  9019. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  9020. var dropdown = this.dropdown = createElement('select',
  9021. void 0, {
  9022. position: 'absolute',
  9023. width: '1px',
  9024. height: '1px',
  9025. padding: 0,
  9026. border: 0,
  9027. top: '-9999em',
  9028. cursor: 'pointer',
  9029. opacity: 0.0001
  9030. },
  9031. this.div);
  9032. // Prevent page zoom on iPhone
  9033. addEvent(dropdown, 'touchstart', function () {
  9034. dropdown.style.fontSize = '16px';
  9035. });
  9036. // Forward events from select to button
  9037. [
  9038. [H.isMS ? 'mouseover' : 'mouseenter'],
  9039. [H.isMS ? 'mouseout' : 'mouseleave'],
  9040. ['change', 'click']
  9041. ].forEach(function (_a) {
  9042. var from = _a[0],
  9043. to = _a[1];
  9044. addEvent(dropdown, from, function () {
  9045. var button = buttons[_this.currentButtonIndex()];
  9046. if (button) {
  9047. fireEvent(button.element, to || from);
  9048. }
  9049. });
  9050. });
  9051. this.zoomText = renderer
  9052. .text(lang.rangeSelectorZoom, 0, 15)
  9053. .add(this.buttonGroup);
  9054. if (!this.chart.styledMode) {
  9055. this.zoomText.css(options.labelStyle);
  9056. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  9057. }
  9058. createElement('option', {
  9059. textContent: this.zoomText.textStr,
  9060. disabled: true
  9061. }, void 0, dropdown);
  9062. this.buttonOptions.forEach(function (rangeOptions, i) {
  9063. createElement('option', {
  9064. textContent: rangeOptions.title || rangeOptions.text
  9065. }, void 0, dropdown);
  9066. buttons[i] = renderer
  9067. .button(rangeOptions.text, 0, 0, function (e) {
  9068. // extract events from button object and call
  9069. var buttonEvents = (rangeOptions.events &&
  9070. rangeOptions.events.click),
  9071. callDefaultEvent;
  9072. if (buttonEvents) {
  9073. callDefaultEvent =
  9074. buttonEvents.call(rangeOptions, e);
  9075. }
  9076. if (callDefaultEvent !== false) {
  9077. _this.clickButton(i);
  9078. }
  9079. _this.isActive = true;
  9080. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  9081. .attr({
  9082. 'text-align': 'center',
  9083. width: width
  9084. })
  9085. .add(_this.buttonGroup);
  9086. if (rangeOptions.title) {
  9087. buttons[i].attr('title', rangeOptions.title);
  9088. }
  9089. });
  9090. };
  9091. /**
  9092. * Align the elements horizontally and vertically.
  9093. *
  9094. * @private
  9095. * @function Highcharts.RangeSelector#alignElements
  9096. * @return {void}
  9097. */
  9098. RangeSelector.prototype.alignElements = function () {
  9099. var _this = this;
  9100. var _a = this,
  9101. buttonGroup = _a.buttonGroup,
  9102. buttons = _a.buttons,
  9103. chart = _a.chart,
  9104. group = _a.group,
  9105. inputGroup = _a.inputGroup,
  9106. options = _a.options,
  9107. zoomText = _a.zoomText;
  9108. var chartOptions = chart.options;
  9109. var navButtonOptions = (chartOptions.exporting &&
  9110. chartOptions.exporting.enabled !== false &&
  9111. chartOptions.navigation &&
  9112. chartOptions.navigation.buttonOptions);
  9113. var buttonPosition = options.buttonPosition,
  9114. inputPosition = options.inputPosition,
  9115. verticalAlign = options.verticalAlign;
  9116. // Get the X offset required to avoid overlapping with the exporting
  9117. // button. This is is used both by the buttonGroup and the inputGroup.
  9118. var getXOffsetForExportButton = function (group,
  9119. position) {
  9120. if (navButtonOptions &&
  9121. _this.titleCollision(chart) &&
  9122. verticalAlign === 'top' &&
  9123. position.align === 'right' && ((position.y -
  9124. group.getBBox().height - 12) <
  9125. ((navButtonOptions.y || 0) +
  9126. (navButtonOptions.height || 0) +
  9127. chart.spacing[0]))) {
  9128. return -40;
  9129. }
  9130. return 0;
  9131. };
  9132. var plotLeft = chart.plotLeft;
  9133. if (group && buttonPosition && inputPosition) {
  9134. var translateX = buttonPosition.x - chart.spacing[3];
  9135. if (buttonGroup) {
  9136. this.positionButtons();
  9137. if (!this.initialButtonGroupWidth) {
  9138. var width_1 = 0;
  9139. if (zoomText) {
  9140. width_1 += zoomText.getBBox().width + 5;
  9141. }
  9142. buttons.forEach(function (button, i) {
  9143. width_1 += button.width;
  9144. if (i !== buttons.length - 1) {
  9145. width_1 += options.buttonSpacing;
  9146. }
  9147. });
  9148. this.initialButtonGroupWidth = width_1;
  9149. }
  9150. plotLeft -= chart.spacing[3];
  9151. this.updateButtonStates();
  9152. // Detect collision between button group and exporting
  9153. var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
  9154. buttonPosition);
  9155. this.alignButtonGroup(xOffsetForExportButton_1);
  9156. // Skip animation
  9157. group.placed = buttonGroup.placed = chart.hasLoaded;
  9158. }
  9159. var xOffsetForExportButton = 0;
  9160. if (inputGroup) {
  9161. // Detect collision between the input group and exporting button
  9162. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  9163. if (inputPosition.align === 'left') {
  9164. translateX = plotLeft;
  9165. }
  9166. else if (inputPosition.align === 'right') {
  9167. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  9168. }
  9169. // Update the alignment to the updated spacing box
  9170. inputGroup.align({
  9171. y: inputPosition.y,
  9172. width: inputGroup.getBBox().width,
  9173. align: inputPosition.align,
  9174. // fix wrong getBBox() value on right align
  9175. x: inputPosition.x + translateX - 2
  9176. }, true, chart.spacingBox);
  9177. // Skip animation
  9178. inputGroup.placed = chart.hasLoaded;
  9179. }
  9180. this.handleCollision(xOffsetForExportButton);
  9181. // Vertical align
  9182. group.align({
  9183. verticalAlign: verticalAlign
  9184. }, true, chart.spacingBox);
  9185. var alignTranslateY = group.alignAttr.translateY;
  9186. // Set position
  9187. var groupHeight = group.getBBox().height + 20; // # 20 padding
  9188. var translateY = 0;
  9189. // Calculate bottom position
  9190. if (verticalAlign === 'bottom') {
  9191. var legendOptions = chart.legend && chart.legend.options;
  9192. var legendHeight = (legendOptions &&
  9193. legendOptions.verticalAlign === 'bottom' &&
  9194. legendOptions.enabled &&
  9195. !legendOptions.floating ?
  9196. (chart.legend.legendHeight +
  9197. pick(legendOptions.margin, 10)) :
  9198. 0);
  9199. groupHeight = groupHeight + legendHeight - 20;
  9200. translateY = (alignTranslateY -
  9201. groupHeight -
  9202. (options.floating ? 0 : options.y) -
  9203. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  9204. 10 // 10 spacing
  9205. );
  9206. }
  9207. if (verticalAlign === 'top') {
  9208. if (options.floating) {
  9209. translateY = 0;
  9210. }
  9211. if (chart.titleOffset && chart.titleOffset[0]) {
  9212. translateY = chart.titleOffset[0];
  9213. }
  9214. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  9215. }
  9216. else if (verticalAlign === 'middle') {
  9217. if (inputPosition.y === buttonPosition.y) {
  9218. translateY = alignTranslateY;
  9219. }
  9220. else if (inputPosition.y || buttonPosition.y) {
  9221. if (inputPosition.y < 0 ||
  9222. buttonPosition.y < 0) {
  9223. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  9224. }
  9225. else {
  9226. translateY = alignTranslateY - groupHeight;
  9227. }
  9228. }
  9229. }
  9230. group.translate(options.x, options.y + Math.floor(translateY));
  9231. // Translate HTML inputs
  9232. var _b = this,
  9233. minInput = _b.minInput,
  9234. maxInput = _b.maxInput,
  9235. dropdown = _b.dropdown;
  9236. if (options.inputEnabled && minInput && maxInput) {
  9237. minInput.style.marginTop = group.translateY + 'px';
  9238. maxInput.style.marginTop = group.translateY + 'px';
  9239. }
  9240. if (dropdown) {
  9241. dropdown.style.marginTop = group.translateY + 'px';
  9242. }
  9243. }
  9244. };
  9245. /**
  9246. * Align the button group horizontally and vertically.
  9247. *
  9248. * @private
  9249. * @function Highcharts.RangeSelector#alignButtonGroup
  9250. * @param {number} xOffsetForExportButton
  9251. * @param {number} [width]
  9252. * @return {void}
  9253. */
  9254. RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
  9255. var _a = this,
  9256. chart = _a.chart,
  9257. options = _a.options,
  9258. buttonGroup = _a.buttonGroup,
  9259. buttons = _a.buttons;
  9260. var buttonPosition = options.buttonPosition;
  9261. var plotLeft = chart.plotLeft - chart.spacing[3];
  9262. var translateX = buttonPosition.x - chart.spacing[3];
  9263. if (buttonPosition.align === 'right') {
  9264. translateX += xOffsetForExportButton - plotLeft; // #13014
  9265. }
  9266. else if (buttonPosition.align === 'center') {
  9267. translateX -= plotLeft / 2;
  9268. }
  9269. if (buttonGroup) {
  9270. // Align button group
  9271. buttonGroup.align({
  9272. y: buttonPosition.y,
  9273. width: pick(width, this.initialButtonGroupWidth),
  9274. align: buttonPosition.align,
  9275. x: translateX
  9276. }, true, chart.spacingBox);
  9277. }
  9278. };
  9279. /**
  9280. * @private
  9281. * @function Highcharts.RangeSelector#positionButtons
  9282. * @return {void}
  9283. */
  9284. RangeSelector.prototype.positionButtons = function () {
  9285. var _a = this,
  9286. buttons = _a.buttons,
  9287. chart = _a.chart,
  9288. options = _a.options,
  9289. zoomText = _a.zoomText;
  9290. var verb = chart.hasLoaded ? 'animate' : 'attr';
  9291. var buttonPosition = options.buttonPosition;
  9292. var plotLeft = chart.plotLeft;
  9293. var buttonLeft = plotLeft;
  9294. if (zoomText && zoomText.visibility !== 'hidden') {
  9295. // #8769, allow dynamically updating margins
  9296. zoomText[verb]({
  9297. x: pick(plotLeft + buttonPosition.x, plotLeft)
  9298. });
  9299. // Button start position
  9300. buttonLeft += buttonPosition.x +
  9301. zoomText.getBBox().width + 5;
  9302. }
  9303. this.buttonOptions.forEach(function (rangeOptions, i) {
  9304. if (buttons[i].visibility !== 'hidden') {
  9305. buttons[i][verb]({ x: buttonLeft });
  9306. // increase button position for the next button
  9307. buttonLeft += buttons[i].width + options.buttonSpacing;
  9308. }
  9309. else {
  9310. buttons[i][verb]({ x: plotLeft });
  9311. }
  9312. });
  9313. };
  9314. /**
  9315. * Handle collision between the button group and the input group
  9316. *
  9317. * @private
  9318. * @function Highcharts.RangeSelector#handleCollision
  9319. *
  9320. * @param {number} xOffsetForExportButton
  9321. * The X offset of the group required to make room for the
  9322. * exporting button
  9323. * @return {void}
  9324. */
  9325. RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
  9326. var _this = this;
  9327. var _a = this,
  9328. chart = _a.chart,
  9329. buttonGroup = _a.buttonGroup,
  9330. inputGroup = _a.inputGroup;
  9331. var _b = this.options,
  9332. buttonPosition = _b.buttonPosition,
  9333. dropdown = _b.dropdown,
  9334. inputPosition = _b.inputPosition;
  9335. var maxButtonWidth = function () {
  9336. var buttonWidth = 0;
  9337. _this.buttons.forEach(function (button) {
  9338. var bBox = button.getBBox();
  9339. if (bBox.width > buttonWidth) {
  9340. buttonWidth = bBox.width;
  9341. }
  9342. });
  9343. return buttonWidth;
  9344. };
  9345. var groupsOverlap = function (buttonGroupWidth) {
  9346. if (inputGroup && buttonGroup) {
  9347. var inputGroupX = (inputGroup.alignAttr.translateX +
  9348. inputGroup.alignOptions.x -
  9349. xOffsetForExportButton +
  9350. // getBBox for detecing left margin
  9351. inputGroup.getBBox().x +
  9352. // 2px padding to not overlap input and label
  9353. 2);
  9354. var inputGroupWidth = inputGroup.alignOptions.width;
  9355. var buttonGroupX = buttonGroup.alignAttr.translateX +
  9356. buttonGroup.getBBox().x;
  9357. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  9358. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  9359. (buttonPosition.y <
  9360. (inputPosition.y +
  9361. inputGroup.getBBox().height));
  9362. }
  9363. return false;
  9364. };
  9365. var moveInputsDown = function () {
  9366. if (inputGroup && buttonGroup) {
  9367. inputGroup.attr({
  9368. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  9369. 0 :
  9370. -xOffsetForExportButton),
  9371. translateY: inputGroup.alignAttr.translateY +
  9372. buttonGroup.getBBox().height + 10
  9373. });
  9374. }
  9375. };
  9376. if (buttonGroup) {
  9377. if (dropdown === 'always') {
  9378. this.collapseButtons(xOffsetForExportButton);
  9379. if (groupsOverlap(maxButtonWidth())) {
  9380. // Move the inputs down if there is still a collision
  9381. // after collapsing the buttons
  9382. moveInputsDown();
  9383. }
  9384. return;
  9385. }
  9386. if (dropdown === 'never') {
  9387. this.expandButtons();
  9388. }
  9389. }
  9390. // Detect collision
  9391. if (inputGroup && buttonGroup) {
  9392. if ((inputPosition.align === buttonPosition.align) ||
  9393. // 20 is minimal spacing between elements
  9394. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  9395. if (dropdown === 'responsive') {
  9396. this.collapseButtons(xOffsetForExportButton);
  9397. if (groupsOverlap(maxButtonWidth())) {
  9398. moveInputsDown();
  9399. }
  9400. }
  9401. else {
  9402. moveInputsDown();
  9403. }
  9404. }
  9405. else if (dropdown === 'responsive') {
  9406. this.expandButtons();
  9407. }
  9408. }
  9409. else if (buttonGroup && dropdown === 'responsive') {
  9410. if (this.initialButtonGroupWidth > chart.plotWidth) {
  9411. this.collapseButtons(xOffsetForExportButton);
  9412. }
  9413. else {
  9414. this.expandButtons();
  9415. }
  9416. }
  9417. };
  9418. /**
  9419. * Collapse the buttons and put the select element on top.
  9420. *
  9421. * @private
  9422. * @function Highcharts.RangeSelector#collapseButtons
  9423. * @param {number} xOffsetForExportButton
  9424. * @return {void}
  9425. */
  9426. RangeSelector.prototype.collapseButtons = function (xOffsetForExportButton) {
  9427. var _a;
  9428. var _b = this,
  9429. buttons = _b.buttons,
  9430. buttonOptions = _b.buttonOptions,
  9431. dropdown = _b.dropdown,
  9432. options = _b.options,
  9433. zoomText = _b.zoomText;
  9434. var getAttribs = function (text) { return ({
  9435. text: text ? text + " \u25BE" : '▾',
  9436. width: 'auto',
  9437. paddingLeft: 8,
  9438. paddingRight: 8
  9439. }); };
  9440. if (zoomText) {
  9441. zoomText.hide();
  9442. }
  9443. var hasActiveButton = false;
  9444. buttonOptions.forEach(function (rangeOptions, i) {
  9445. var button = buttons[i];
  9446. if (button.state !== 2) {
  9447. button.hide();
  9448. }
  9449. else {
  9450. button.show();
  9451. button.attr(getAttribs(rangeOptions.text));
  9452. hasActiveButton = true;
  9453. }
  9454. });
  9455. if (!hasActiveButton) {
  9456. if (dropdown) {
  9457. dropdown.selectedIndex = 0;
  9458. }
  9459. buttons[0].show();
  9460. buttons[0].attr(getAttribs((_a = this.zoomText) === null || _a === void 0 ? void 0 : _a.textStr));
  9461. }
  9462. var align = options.buttonPosition.align;
  9463. this.positionButtons();
  9464. if (align === 'right' || align === 'center') {
  9465. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  9466. }
  9467. this.showDropdown();
  9468. };
  9469. /**
  9470. * Show all the buttons and hide the select element.
  9471. *
  9472. * @private
  9473. * @function Highcharts.RangeSelector#expandButtons
  9474. * @return {void}
  9475. */
  9476. RangeSelector.prototype.expandButtons = function () {
  9477. var _a = this,
  9478. buttons = _a.buttons,
  9479. buttonOptions = _a.buttonOptions,
  9480. options = _a.options,
  9481. zoomText = _a.zoomText;
  9482. this.hideDropdown();
  9483. if (zoomText) {
  9484. zoomText.show();
  9485. }
  9486. buttonOptions.forEach(function (rangeOptions, i) {
  9487. var button = buttons[i];
  9488. button.show();
  9489. button.attr({
  9490. text: rangeOptions.text,
  9491. width: options.buttonTheme.width || 28,
  9492. paddingLeft: 'unset',
  9493. paddingRight: 'unset'
  9494. });
  9495. if (button.state < 2) {
  9496. button.setState(0);
  9497. }
  9498. });
  9499. this.positionButtons();
  9500. };
  9501. /**
  9502. * Get the index of the visible button when the buttons are collapsed.
  9503. *
  9504. * @private
  9505. * @function Highcharts.RangeSelector#currentButtonIndex
  9506. * @return {number}
  9507. */
  9508. RangeSelector.prototype.currentButtonIndex = function () {
  9509. var dropdown = this.dropdown;
  9510. if (dropdown && dropdown.selectedIndex > 0) {
  9511. return dropdown.selectedIndex - 1;
  9512. }
  9513. return 0;
  9514. };
  9515. /**
  9516. * Position the select element on top of the button.
  9517. *
  9518. * @private
  9519. * @function Highcharts.RangeSelector#showDropdown
  9520. * @return {void}
  9521. */
  9522. RangeSelector.prototype.showDropdown = function () {
  9523. var _a = this,
  9524. buttonGroup = _a.buttonGroup,
  9525. buttons = _a.buttons,
  9526. chart = _a.chart,
  9527. dropdown = _a.dropdown;
  9528. if (buttonGroup && dropdown) {
  9529. var translateX = buttonGroup.translateX,
  9530. translateY = buttonGroup.translateY;
  9531. var bBox = buttons[this.currentButtonIndex()].getBBox();
  9532. css(dropdown, {
  9533. left: (chart.plotLeft + translateX) + 'px',
  9534. top: (translateY + 0.5) + 'px',
  9535. width: bBox.width + 'px',
  9536. height: bBox.height + 'px'
  9537. });
  9538. this.hasVisibleDropdown = true;
  9539. }
  9540. };
  9541. /**
  9542. * @private
  9543. * @function Highcharts.RangeSelector#hideDropdown
  9544. * @return {void}
  9545. */
  9546. RangeSelector.prototype.hideDropdown = function () {
  9547. var dropdown = this.dropdown;
  9548. if (dropdown) {
  9549. css(dropdown, {
  9550. top: '-9999em',
  9551. width: '1px',
  9552. height: '1px'
  9553. });
  9554. this.hasVisibleDropdown = false;
  9555. }
  9556. };
  9557. /**
  9558. * Extracts height of range selector
  9559. *
  9560. * @private
  9561. * @function Highcharts.RangeSelector#getHeight
  9562. * @return {number}
  9563. * Returns rangeSelector height
  9564. */
  9565. RangeSelector.prototype.getHeight = function () {
  9566. var rangeSelector = this,
  9567. options = rangeSelector.options,
  9568. rangeSelectorGroup = rangeSelector.group,
  9569. inputPosition = options.inputPosition,
  9570. buttonPosition = options.buttonPosition,
  9571. yPosition = options.y,
  9572. buttonPositionY = buttonPosition.y,
  9573. inputPositionY = inputPosition.y,
  9574. rangeSelectorHeight = 0,
  9575. minPosition;
  9576. if (options.height) {
  9577. return options.height;
  9578. }
  9579. // Align the elements before we read the height in case we're switching
  9580. // between wrapped and non-wrapped layout
  9581. this.alignElements();
  9582. rangeSelectorHeight = rangeSelectorGroup ?
  9583. // 13px to keep back compatibility
  9584. (rangeSelectorGroup.getBBox(true).height) + 13 +
  9585. yPosition :
  9586. 0;
  9587. minPosition = Math.min(inputPositionY, buttonPositionY);
  9588. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  9589. (inputPositionY > 0 && buttonPositionY > 0)) {
  9590. rangeSelectorHeight += Math.abs(minPosition);
  9591. }
  9592. return rangeSelectorHeight;
  9593. };
  9594. /**
  9595. * Detect collision with title or subtitle
  9596. *
  9597. * @private
  9598. * @function Highcharts.RangeSelector#titleCollision
  9599. *
  9600. * @param {Highcharts.Chart} chart
  9601. *
  9602. * @return {boolean}
  9603. * Returns collision status
  9604. */
  9605. RangeSelector.prototype.titleCollision = function (chart) {
  9606. return !(chart.options.title.text ||
  9607. chart.options.subtitle.text);
  9608. };
  9609. /**
  9610. * Update the range selector with new options
  9611. *
  9612. * @private
  9613. * @function Highcharts.RangeSelector#update
  9614. * @param {Highcharts.RangeSelectorOptions} options
  9615. * @return {void}
  9616. */
  9617. RangeSelector.prototype.update = function (options) {
  9618. var chart = this.chart;
  9619. merge(true, chart.options.rangeSelector, options);
  9620. this.destroy();
  9621. this.init(chart);
  9622. this.render();
  9623. };
  9624. /**
  9625. * Destroys allocated elements.
  9626. *
  9627. * @private
  9628. * @function Highcharts.RangeSelector#destroy
  9629. */
  9630. RangeSelector.prototype.destroy = function () {
  9631. var rSelector = this,
  9632. minInput = rSelector.minInput,
  9633. maxInput = rSelector.maxInput;
  9634. if (rSelector.eventsToUnbind) {
  9635. rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
  9636. rSelector.eventsToUnbind = void 0;
  9637. }
  9638. // Destroy elements in collections
  9639. destroyObjectProperties(rSelector.buttons);
  9640. // Clear input element events
  9641. if (minInput) {
  9642. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  9643. }
  9644. if (maxInput) {
  9645. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  9646. }
  9647. // Destroy HTML and SVG elements
  9648. objectEach(rSelector, function (val, key) {
  9649. if (val && key !== 'chart') {
  9650. if (val instanceof SVGElement) {
  9651. // SVGElement
  9652. val.destroy();
  9653. }
  9654. else if (val instanceof window.HTMLElement) {
  9655. // HTML element
  9656. discardElement(val);
  9657. }
  9658. }
  9659. if (val !== RangeSelector.prototype[key]) {
  9660. rSelector[key] = null;
  9661. }
  9662. }, this);
  9663. };
  9664. return RangeSelector;
  9665. }());
  9666. /**
  9667. * The default buttons for pre-selecting time frames
  9668. */
  9669. RangeSelector.prototype.defaultButtons = [{
  9670. type: 'month',
  9671. count: 1,
  9672. text: '1m',
  9673. title: 'View 1 month'
  9674. }, {
  9675. type: 'month',
  9676. count: 3,
  9677. text: '3m',
  9678. title: 'View 3 months'
  9679. }, {
  9680. type: 'month',
  9681. count: 6,
  9682. text: '6m',
  9683. title: 'View 6 months'
  9684. }, {
  9685. type: 'ytd',
  9686. text: 'YTD',
  9687. title: 'View year to date'
  9688. }, {
  9689. type: 'year',
  9690. count: 1,
  9691. text: '1y',
  9692. title: 'View 1 year'
  9693. }, {
  9694. type: 'all',
  9695. text: 'All',
  9696. title: 'View all'
  9697. }];
  9698. /**
  9699. * The date formats to use when setting min, max and value on date inputs
  9700. */
  9701. RangeSelector.prototype.inputTypeFormats = {
  9702. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  9703. 'date': '%Y-%m-%d',
  9704. 'time': '%H:%M:%S'
  9705. };
  9706. /**
  9707. * Get the preferred input type based on a date format string.
  9708. *
  9709. * @private
  9710. * @function preferredInputType
  9711. * @param {string} format
  9712. * @return {string}
  9713. */
  9714. function preferredInputType(format) {
  9715. var ms = format.indexOf('%L') !== -1;
  9716. if (ms) {
  9717. return 'text';
  9718. }
  9719. var date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y'].some(function (char) {
  9720. return format.indexOf('%' + char) !== -1;
  9721. });
  9722. var time = ['H', 'k', 'I', 'l', 'M', 'S'].some(function (char) {
  9723. return format.indexOf('%' + char) !== -1;
  9724. });
  9725. if (date && time) {
  9726. return 'datetime-local';
  9727. }
  9728. if (date) {
  9729. return 'date';
  9730. }
  9731. if (time) {
  9732. return 'time';
  9733. }
  9734. return 'text';
  9735. }
  9736. /**
  9737. * Get the axis min value based on the range option and the current max. For
  9738. * stock charts this is extended via the {@link RangeSelector} so that if the
  9739. * selected range is a multiple of months or years, it is compensated for
  9740. * various month lengths.
  9741. *
  9742. * @private
  9743. * @function Highcharts.Axis#minFromRange
  9744. * @return {number|undefined}
  9745. * The new minimum value.
  9746. */
  9747. Axis.prototype.minFromRange = function () {
  9748. var rangeOptions = this.range,
  9749. type = rangeOptions.type,
  9750. min,
  9751. max = this.max,
  9752. dataMin,
  9753. range,
  9754. time = this.chart.time,
  9755. // Get the true range from a start date
  9756. getTrueRange = function (base,
  9757. count) {
  9758. var timeName = type === 'year' ? 'FullYear' : 'Month';
  9759. var date = new time.Date(base);
  9760. var basePeriod = time.get(timeName,
  9761. date);
  9762. time.set(timeName, date, basePeriod + count);
  9763. if (basePeriod === time.get(timeName, date)) {
  9764. time.set('Date', date, 0); // #6537
  9765. }
  9766. return date.getTime() - base;
  9767. };
  9768. if (isNumber(rangeOptions)) {
  9769. min = max - rangeOptions;
  9770. range = rangeOptions;
  9771. }
  9772. else {
  9773. min = max + getTrueRange(max, -rangeOptions.count);
  9774. // Let the fixedRange reflect initial settings (#5930)
  9775. if (this.chart) {
  9776. this.chart.fixedRange = max - min;
  9777. }
  9778. }
  9779. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  9780. if (!isNumber(min)) {
  9781. min = dataMin;
  9782. }
  9783. if (min <= dataMin) {
  9784. min = dataMin;
  9785. if (typeof range === 'undefined') { // #4501
  9786. range = getTrueRange(min, rangeOptions.count);
  9787. }
  9788. this.newMax = Math.min(min + range, this.dataMax);
  9789. }
  9790. if (!isNumber(max)) {
  9791. min = void 0;
  9792. }
  9793. return min;
  9794. };
  9795. if (!H.RangeSelector) {
  9796. var chartDestroyEvents_1 = [];
  9797. var initRangeSelector_1 = function (chart) {
  9798. var extremes,
  9799. rangeSelector = chart.rangeSelector,
  9800. legend,
  9801. alignTo,
  9802. verticalAlign;
  9803. /**
  9804. * @private
  9805. */
  9806. function render() {
  9807. if (rangeSelector) {
  9808. extremes = chart.xAxis[0].getExtremes();
  9809. legend = chart.legend;
  9810. verticalAlign = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options.verticalAlign;
  9811. if (isNumber(extremes.min)) {
  9812. rangeSelector.render(extremes.min, extremes.max);
  9813. }
  9814. // Re-align the legend so that it's below the rangeselector
  9815. if (legend.display &&
  9816. verticalAlign === 'top' &&
  9817. verticalAlign === legend.options.verticalAlign) {
  9818. // Create a new alignment box for the legend.
  9819. alignTo = merge(chart.spacingBox);
  9820. if (legend.options.layout === 'vertical') {
  9821. alignTo.y = chart.plotTop;
  9822. }
  9823. else {
  9824. alignTo.y += rangeSelector.getHeight();
  9825. }
  9826. legend.group.placed = false; // Don't animate the alignment.
  9827. legend.align(alignTo);
  9828. }
  9829. }
  9830. }
  9831. if (rangeSelector) {
  9832. var events = find(chartDestroyEvents_1,
  9833. function (e) { return e[0] === chart; });
  9834. if (!events) {
  9835. chartDestroyEvents_1.push([chart, [
  9836. // redraw the scroller on setExtremes
  9837. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  9838. if (rangeSelector) {
  9839. rangeSelector.render(e.min, e.max);
  9840. }
  9841. }),
  9842. // redraw the scroller chart resize
  9843. addEvent(chart, 'redraw', render)
  9844. ]]);
  9845. }
  9846. // do it now
  9847. render();
  9848. }
  9849. };
  9850. // Initialize rangeselector for stock charts
  9851. addEvent(Chart, 'afterGetContainer', function () {
  9852. var _a;
  9853. if ((_a = this.options.rangeSelector) === null || _a === void 0 ? void 0 : _a.enabled) {
  9854. this.rangeSelector = new RangeSelector(this);
  9855. }
  9856. });
  9857. addEvent(Chart, 'beforeRender', function () {
  9858. var chart = this,
  9859. axes = chart.axes,
  9860. rangeSelector = chart.rangeSelector,
  9861. verticalAlign;
  9862. if (rangeSelector) {
  9863. if (isNumber(rangeSelector.deferredYTDClick)) {
  9864. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  9865. delete rangeSelector.deferredYTDClick;
  9866. }
  9867. axes.forEach(function (axis) {
  9868. axis.updateNames();
  9869. axis.setScale();
  9870. });
  9871. chart.getAxisMargins();
  9872. rangeSelector.render();
  9873. verticalAlign = rangeSelector.options.verticalAlign;
  9874. if (!rangeSelector.options.floating) {
  9875. if (verticalAlign === 'bottom') {
  9876. this.extraBottomMargin = true;
  9877. }
  9878. else if (verticalAlign !== 'middle') {
  9879. this.extraTopMargin = true;
  9880. }
  9881. }
  9882. }
  9883. });
  9884. addEvent(Chart, 'update', function (e) {
  9885. var chart = this,
  9886. options = e.options,
  9887. optionsRangeSelector = options.rangeSelector,
  9888. rangeSelector = chart.rangeSelector,
  9889. verticalAlign,
  9890. extraBottomMarginWas = this.extraBottomMargin,
  9891. extraTopMarginWas = this.extraTopMargin;
  9892. if (optionsRangeSelector &&
  9893. optionsRangeSelector.enabled &&
  9894. !defined(rangeSelector) &&
  9895. this.options.rangeSelector) {
  9896. this.options.rangeSelector.enabled = true;
  9897. this.rangeSelector = rangeSelector = new RangeSelector(this);
  9898. }
  9899. this.extraBottomMargin = false;
  9900. this.extraTopMargin = false;
  9901. if (rangeSelector) {
  9902. initRangeSelector_1(this);
  9903. verticalAlign = (optionsRangeSelector &&
  9904. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  9905. if (!rangeSelector.options.floating) {
  9906. if (verticalAlign === 'bottom') {
  9907. this.extraBottomMargin = true;
  9908. }
  9909. else if (verticalAlign !== 'middle') {
  9910. this.extraTopMargin = true;
  9911. }
  9912. }
  9913. if (this.extraBottomMargin !== extraBottomMarginWas ||
  9914. this.extraTopMargin !== extraTopMarginWas) {
  9915. this.isDirtyBox = true;
  9916. }
  9917. }
  9918. });
  9919. addEvent(Chart, 'render', function () {
  9920. var chart = this,
  9921. rangeSelector = chart.rangeSelector,
  9922. verticalAlign;
  9923. if (rangeSelector && !rangeSelector.options.floating) {
  9924. rangeSelector.render();
  9925. verticalAlign = rangeSelector.options.verticalAlign;
  9926. if (verticalAlign === 'bottom') {
  9927. this.extraBottomMargin = true;
  9928. }
  9929. else if (verticalAlign !== 'middle') {
  9930. this.extraTopMargin = true;
  9931. }
  9932. }
  9933. });
  9934. addEvent(Chart, 'getMargins', function () {
  9935. var rangeSelector = this.rangeSelector,
  9936. rangeSelectorHeight;
  9937. if (rangeSelector) {
  9938. rangeSelectorHeight = rangeSelector.getHeight();
  9939. if (this.extraTopMargin) {
  9940. this.plotTop += rangeSelectorHeight;
  9941. }
  9942. if (this.extraBottomMargin) {
  9943. this.marginBottom += rangeSelectorHeight;
  9944. }
  9945. }
  9946. });
  9947. Chart.prototype.callbacks.push(initRangeSelector_1);
  9948. // Remove resize/afterSetExtremes at chart destroy
  9949. addEvent(Chart, 'destroy', function destroyEvents() {
  9950. for (var i = 0; i < chartDestroyEvents_1.length; i++) {
  9951. var events = chartDestroyEvents_1[i];
  9952. if (events[0] === this) {
  9953. events[1].forEach(function (unbind) { return unbind(); });
  9954. chartDestroyEvents_1.splice(i, 1);
  9955. return;
  9956. }
  9957. }
  9958. });
  9959. H.RangeSelector = RangeSelector;
  9960. }
  9961. return H.RangeSelector;
  9962. });
  9963. _registerModule(_modules, 'Core/Chart/StockChart.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.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 (Axis, Chart, H, palette, Point, Series, SVGRenderer, U) {
  9964. /* *
  9965. *
  9966. * (c) 2010-2021 Torstein Honsi
  9967. *
  9968. * License: www.highcharts.com/license
  9969. *
  9970. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9971. *
  9972. * */
  9973. var pointTooltipFormatter = Point.prototype.tooltipFormatter;
  9974. var _a = Series.prototype,
  9975. seriesInit = _a.init,
  9976. seriesProcessData = _a.processData;
  9977. var addEvent = U.addEvent,
  9978. arrayMax = U.arrayMax,
  9979. arrayMin = U.arrayMin,
  9980. clamp = U.clamp,
  9981. defined = U.defined,
  9982. extend = U.extend,
  9983. find = U.find,
  9984. format = U.format,
  9985. getOptions = U.getOptions,
  9986. isNumber = U.isNumber,
  9987. isString = U.isString,
  9988. merge = U.merge,
  9989. pick = U.pick,
  9990. splat = U.splat;
  9991. // Has a dependency on Navigator due to the use of
  9992. // defaultOptions.navigator
  9993. // Has a dependency on Scrollbar due to the use of
  9994. // defaultOptions.scrollbar
  9995. // Has a dependency on RangeSelector due to the use of
  9996. // defaultOptions.rangeSelector
  9997. /* eslint-disable no-invalid-this, valid-jsdoc */
  9998. /* *
  9999. *
  10000. * Factory
  10001. *
  10002. * */
  10003. /**
  10004. * Factory function for creating new stock charts. Creates a new
  10005. * {@link Highcharts.Chart|Chart} object with different default options than the
  10006. * basic Chart.
  10007. *
  10008. * @example
  10009. * var chart = Highcharts.stockChart('container', {
  10010. * series: [{
  10011. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  10012. * pointInterval: 24 * 60 * 60 * 1000
  10013. * }]
  10014. * });
  10015. *
  10016. * @function Highcharts.stockChart
  10017. *
  10018. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  10019. * The DOM element to render to, or its id.
  10020. *
  10021. * @param {Highcharts.Options} options
  10022. * The chart options structure as described in the
  10023. * [options reference](https://api.highcharts.com/highstock).
  10024. *
  10025. * @param {Highcharts.ChartCallbackFunction} [callback]
  10026. * A function to execute when the chart object is finished loading and
  10027. * rendering. In most cases the chart is built in one thread, but in
  10028. * Internet Explorer version 8 or less the chart is sometimes
  10029. * initialized before the document is ready, and in these cases the
  10030. * chart object will not be finished synchronously. As a consequence,
  10031. * code that relies on the newly built Chart object should always run in
  10032. * the callback. Defining a
  10033. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  10034. * handler is equivalent.
  10035. *
  10036. * @return {Highcharts.Chart}
  10037. * The chart object.
  10038. */
  10039. function stockChart(a, b, c) {
  10040. var hasRenderToArg = isString(a) || a.nodeName,
  10041. options = arguments[hasRenderToArg ? 1 : 0],
  10042. userOptions = options,
  10043. // to increase performance, don't merge the data
  10044. seriesOptions = options.series,
  10045. defaultOptions = getOptions(),
  10046. opposite,
  10047. // Always disable startOnTick:true on the main axis when the navigator
  10048. // is enabled (#1090)
  10049. navigatorEnabled = pick(options.navigator && options.navigator.enabled,
  10050. defaultOptions.navigator.enabled,
  10051. true);
  10052. // apply X axis options to both single and multi y axes
  10053. options.xAxis = splat(options.xAxis || {}).map(function (xAxisOptions, i) {
  10054. return merge({
  10055. minPadding: 0,
  10056. maxPadding: 0,
  10057. overscroll: 0,
  10058. ordinal: true,
  10059. title: {
  10060. text: null
  10061. },
  10062. labels: {
  10063. overflow: 'justify'
  10064. },
  10065. showLastLabel: true
  10066. }, defaultOptions.xAxis, // #3802
  10067. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  10068. xAxisOptions, // user options
  10069. {
  10070. type: 'datetime',
  10071. categories: null
  10072. }, (navigatorEnabled ? {
  10073. startOnTick: false,
  10074. endOnTick: false
  10075. } : null));
  10076. });
  10077. // apply Y axis options to both single and multi y axes
  10078. options.yAxis = splat(options.yAxis || {}).map(function (yAxisOptions, i) {
  10079. opposite = pick(yAxisOptions.opposite, true);
  10080. return merge({
  10081. labels: {
  10082. y: -2
  10083. },
  10084. opposite: opposite,
  10085. /**
  10086. * @default {highcharts} true
  10087. * @default {highstock} false
  10088. * @apioption yAxis.showLastLabel
  10089. *
  10090. * @private
  10091. */
  10092. showLastLabel: !!(
  10093. // #6104, show last label by default for category axes
  10094. yAxisOptions.categories ||
  10095. yAxisOptions.type === 'category'),
  10096. title: {
  10097. text: null
  10098. }
  10099. }, defaultOptions.yAxis, // #3802
  10100. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  10101. yAxisOptions // user options
  10102. );
  10103. });
  10104. options.series = null;
  10105. options = merge({
  10106. chart: {
  10107. panning: {
  10108. enabled: true,
  10109. type: 'x'
  10110. },
  10111. pinchType: 'x'
  10112. },
  10113. navigator: {
  10114. enabled: navigatorEnabled
  10115. },
  10116. scrollbar: {
  10117. // #4988 - check if setOptions was called
  10118. enabled: pick(defaultOptions.scrollbar.enabled, true)
  10119. },
  10120. rangeSelector: {
  10121. // #4988 - check if setOptions was called
  10122. enabled: pick(defaultOptions.rangeSelector.enabled, true)
  10123. },
  10124. title: {
  10125. text: null
  10126. },
  10127. tooltip: {
  10128. split: pick(defaultOptions.tooltip.split, true),
  10129. crosshairs: true
  10130. },
  10131. legend: {
  10132. enabled: false
  10133. }
  10134. }, options, // user's options
  10135. {
  10136. isStock: true // internal flag
  10137. });
  10138. options.series = userOptions.series = seriesOptions;
  10139. return hasRenderToArg ?
  10140. new Chart(a, options, c) :
  10141. new Chart(options, b);
  10142. }
  10143. /* *
  10144. *
  10145. * Compositions
  10146. *
  10147. * */
  10148. // Handle som Stock-specific series defaults, override the plotOptions before
  10149. // series options are handled.
  10150. addEvent(Series, 'setOptions', function (e) {
  10151. var overrides;
  10152. if (this.chart.options.isStock) {
  10153. if (this.is('column') || this.is('columnrange')) {
  10154. overrides = {
  10155. borderWidth: 0,
  10156. shadow: false
  10157. };
  10158. }
  10159. else if (!this.is('scatter') && !this.is('sma')) {
  10160. overrides = {
  10161. marker: {
  10162. enabled: false,
  10163. radius: 2
  10164. }
  10165. };
  10166. }
  10167. if (overrides) {
  10168. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  10169. }
  10170. }
  10171. });
  10172. // Override the automatic label alignment so that the first Y axis' labels
  10173. // are drawn on top of the grid line, and subsequent axes are drawn outside
  10174. addEvent(Axis, 'autoLabelAlign', function (e) {
  10175. var chart = this.chart,
  10176. options = this.options,
  10177. panes = chart._labelPanes = chart._labelPanes || {},
  10178. key,
  10179. labelOptions = this.options.labels;
  10180. if (this.chart.options.isStock && this.coll === 'yAxis') {
  10181. key = options.top + ',' + options.height;
  10182. // do it only for the first Y axis of each pane
  10183. if (!panes[key] && labelOptions.enabled) {
  10184. if (labelOptions.x === 15) { // default
  10185. labelOptions.x = 0;
  10186. }
  10187. if (typeof labelOptions.align === 'undefined') {
  10188. labelOptions.align = 'right';
  10189. }
  10190. panes[key] = this;
  10191. e.align = 'right';
  10192. e.preventDefault();
  10193. }
  10194. }
  10195. });
  10196. // Clear axis from label panes (#6071)
  10197. addEvent(Axis, 'destroy', function () {
  10198. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  10199. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  10200. delete chart._labelPanes[key];
  10201. }
  10202. });
  10203. // Override getPlotLinePath to allow for multipane charts
  10204. addEvent(Axis, 'getPlotLinePath', function (e) {
  10205. var axis = this,
  10206. series = (this.isLinked && !this.series ?
  10207. this.linkedParent.series :
  10208. this.series),
  10209. chart = axis.chart,
  10210. renderer = chart.renderer,
  10211. axisLeft = axis.left,
  10212. axisTop = axis.top,
  10213. x1,
  10214. y1,
  10215. x2,
  10216. y2,
  10217. result = [],
  10218. axes = [], // #3416 need a default array
  10219. axes2,
  10220. uniqueAxes,
  10221. translatedValue = e.translatedValue,
  10222. value = e.value,
  10223. force = e.force,
  10224. transVal;
  10225. /**
  10226. * Return the other axis based on either the axis option or on related
  10227. * series.
  10228. * @private
  10229. */
  10230. function getAxis(coll) {
  10231. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis',
  10232. opt = axis.options[otherColl];
  10233. // Other axis indexed by number
  10234. if (isNumber(opt)) {
  10235. return [chart[otherColl][opt]];
  10236. }
  10237. // Other axis indexed by id (like navigator)
  10238. if (isString(opt)) {
  10239. return [chart.get(opt)];
  10240. }
  10241. // Auto detect based on existing series
  10242. return series.map(function (s) {
  10243. return s[otherColl];
  10244. });
  10245. }
  10246. if ( // For stock chart, by default render paths across the panes
  10247. // except the case when `acrossPanes` is disabled by user (#6644)
  10248. (chart.options.isStock && e.acrossPanes !== false) &&
  10249. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  10250. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  10251. e.preventDefault();
  10252. // Get the related axes based on series
  10253. axes = getAxis(axis.coll);
  10254. // Get the related axes based options.*Axis setting #2810
  10255. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  10256. axes2.forEach(function (A) {
  10257. if (defined(A.options.id) ?
  10258. A.options.id.indexOf('navigator') === -1 :
  10259. true) {
  10260. var a = (A.isXAxis ? 'yAxis' : 'xAxis'),
  10261. rax = (defined(A.options[a]) ?
  10262. chart[a][A.options[a]] :
  10263. chart[a][0]);
  10264. if (axis === rax) {
  10265. axes.push(A);
  10266. }
  10267. }
  10268. });
  10269. // Remove duplicates in the axes array. If there are no axes in the axes
  10270. // array, we are adding an axis without data, so we need to populate
  10271. // this with grid lines (#2796).
  10272. uniqueAxes = axes.length ?
  10273. [] :
  10274. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  10275. axes.forEach(function (axis2) {
  10276. if (uniqueAxes.indexOf(axis2) === -1 &&
  10277. // Do not draw on axis which overlap completely. #5424
  10278. !find(uniqueAxes, function (unique) {
  10279. return unique.pos === axis2.pos && unique.len === axis2.len;
  10280. })) {
  10281. uniqueAxes.push(axis2);
  10282. }
  10283. });
  10284. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  10285. if (isNumber(transVal)) {
  10286. if (axis.horiz) {
  10287. uniqueAxes.forEach(function (axis2) {
  10288. var skip;
  10289. y1 = axis2.pos;
  10290. y2 = y1 + axis2.len;
  10291. x1 = x2 = Math.round(transVal + axis.transB);
  10292. // outside plot area
  10293. if (force !== 'pass' &&
  10294. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  10295. if (force) {
  10296. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  10297. }
  10298. else {
  10299. skip = true;
  10300. }
  10301. }
  10302. if (!skip) {
  10303. result.push(['M', x1, y1], ['L', x2, y2]);
  10304. }
  10305. });
  10306. }
  10307. else {
  10308. uniqueAxes.forEach(function (axis2) {
  10309. var skip;
  10310. x1 = axis2.pos;
  10311. x2 = x1 + axis2.len;
  10312. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  10313. // outside plot area
  10314. if (force !== 'pass' &&
  10315. (y1 < axisTop || y1 > axisTop + axis.height)) {
  10316. if (force) {
  10317. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  10318. }
  10319. else {
  10320. skip = true;
  10321. }
  10322. }
  10323. if (!skip) {
  10324. result.push(['M', x1, y1], ['L', x2, y2]);
  10325. }
  10326. });
  10327. }
  10328. }
  10329. e.path = result.length > 0 ?
  10330. renderer.crispPolyLine(result, e.lineWidth || 1) :
  10331. // #3557 getPlotLinePath in regular Highcharts also returns null
  10332. null;
  10333. }
  10334. });
  10335. /**
  10336. * Function to crisp a line with multiple segments
  10337. *
  10338. * @private
  10339. * @function Highcharts.SVGRenderer#crispPolyLine
  10340. * @param {Highcharts.SVGPathArray} points
  10341. * @param {number} width
  10342. * @return {Highcharts.SVGPathArray}
  10343. */
  10344. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  10345. // points format: [['M', 0, 0], ['L', 100, 0]]
  10346. // normalize to a crisp line
  10347. for (var i = 0; i < points.length; i = i + 2) {
  10348. var start = points[i],
  10349. end = points[i + 1];
  10350. if (start[1] === end[1]) {
  10351. // Substract due to #1129. Now bottom and left axis gridlines behave
  10352. // the same.
  10353. start[1] = end[1] =
  10354. Math.round(start[1]) - (width % 2 / 2);
  10355. }
  10356. if (start[2] === end[2]) {
  10357. start[2] = end[2] =
  10358. Math.round(start[2]) + (width % 2 / 2);
  10359. }
  10360. }
  10361. return points;
  10362. };
  10363. // Wrapper to hide the label
  10364. addEvent(Axis, 'afterHideCrosshair', function () {
  10365. if (this.crossLabel) {
  10366. this.crossLabel = this.crossLabel.hide();
  10367. }
  10368. });
  10369. // Extend crosshairs to also draw the label
  10370. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  10371. // Check if the label has to be drawn
  10372. if (!defined(this.crosshair.label) ||
  10373. !this.crosshair.label.enabled ||
  10374. !this.cross) {
  10375. return;
  10376. }
  10377. var chart = this.chart, log = this.logarithmic, options = this.options.crosshair.label, // the label's options
  10378. horiz = this.horiz, // axis orientation
  10379. opposite = this.opposite, // axis position
  10380. left = this.left, // left position
  10381. top = this.top, // top position
  10382. crossLabel = this.crossLabel, // the svgElement
  10383. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, value, offset = 0,
  10384. // Use last available event (#5287)
  10385. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  10386. if (log) {
  10387. min = log.lin2log(min);
  10388. max = log.lin2log(max);
  10389. }
  10390. align = (horiz ? 'center' : opposite ?
  10391. (this.labelAlign === 'right' ? 'right' : 'left') :
  10392. (this.labelAlign === 'left' ? 'left' : 'center'));
  10393. // If the label does not exist yet, create it.
  10394. if (!crossLabel) {
  10395. crossLabel = this.crossLabel = chart.renderer
  10396. .label(null, null, null, options.shape || 'callout')
  10397. .addClass('highcharts-crosshair-label' + (this.series[0] &&
  10398. ' highcharts-color-' + this.series[0].colorIndex))
  10399. .attr({
  10400. align: options.align || align,
  10401. padding: pick(options.padding, 8),
  10402. r: pick(options.borderRadius, 3),
  10403. zIndex: 2
  10404. })
  10405. .add(this.labelGroup);
  10406. // Presentational
  10407. if (!chart.styledMode) {
  10408. crossLabel
  10409. .attr({
  10410. fill: options.backgroundColor ||
  10411. (this.series[0] && this.series[0].color) ||
  10412. palette.neutralColor60,
  10413. stroke: options.borderColor || '',
  10414. 'stroke-width': options.borderWidth || 0
  10415. })
  10416. .css(extend({
  10417. color: palette.backgroundColor,
  10418. fontWeight: 'normal',
  10419. fontSize: '11px',
  10420. textAlign: 'center'
  10421. }, options.style));
  10422. }
  10423. }
  10424. if (horiz) {
  10425. posx = snap ? point.plotX + left : e.chartX;
  10426. posy = top + (opposite ? 0 : this.height);
  10427. }
  10428. else {
  10429. posx = opposite ? this.width + left : 0;
  10430. posy = snap ? point.plotY + top : e.chartY;
  10431. }
  10432. if (!formatOption && !options.formatter) {
  10433. if (this.dateTime) {
  10434. formatFormat = '%b %d, %Y';
  10435. }
  10436. formatOption =
  10437. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  10438. }
  10439. // Show the label
  10440. value = snap ?
  10441. point[this.isXAxis ? 'x' : 'y'] :
  10442. this.toValue(horiz ? e.chartX : e.chartY);
  10443. crossLabel.attr({
  10444. text: formatOption ?
  10445. format(formatOption, { value: value }, chart) :
  10446. options.formatter.call(this, value),
  10447. x: posx,
  10448. y: posy,
  10449. // Crosshair should be rendered within Axis range (#7219)
  10450. visibility: value < min || value > max ?
  10451. 'hidden' :
  10452. 'visible'
  10453. });
  10454. crossBox = crossLabel.getBBox();
  10455. // now it is placed we can correct its position
  10456. if (isNumber(crossLabel.y)) {
  10457. if (horiz) {
  10458. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  10459. posy = crossLabel.y - crossBox.height;
  10460. }
  10461. }
  10462. else {
  10463. posy = crossLabel.y - (crossBox.height / 2);
  10464. }
  10465. }
  10466. // check the edges
  10467. if (horiz) {
  10468. limit = {
  10469. left: left - crossBox.x,
  10470. right: left + this.width - crossBox.x
  10471. };
  10472. }
  10473. else {
  10474. limit = {
  10475. left: this.labelAlign === 'left' ? left : 0,
  10476. right: this.labelAlign === 'right' ?
  10477. left + this.width :
  10478. chart.chartWidth
  10479. };
  10480. }
  10481. // left edge
  10482. if (crossLabel.translateX < limit.left) {
  10483. offset = limit.left - crossLabel.translateX;
  10484. }
  10485. // right edge
  10486. if (crossLabel.translateX + crossBox.width >= limit.right) {
  10487. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  10488. }
  10489. // show the crosslabel
  10490. crossLabel.attr({
  10491. x: posx + offset,
  10492. y: posy,
  10493. // First set x and y, then anchorX and anchorY, when box is actually
  10494. // calculated, #5702
  10495. anchorX: horiz ?
  10496. posx :
  10497. (this.opposite ? 0 : chart.chartWidth),
  10498. anchorY: horiz ?
  10499. (this.opposite ? chart.chartHeight : 0) :
  10500. posy + crossBox.height / 2
  10501. });
  10502. });
  10503. /* ************************************************************************** *
  10504. * Start value compare logic *
  10505. * ************************************************************************** */
  10506. /**
  10507. * Extend series.init by adding a method to modify the y value used for plotting
  10508. * on the y axis. This method is called both from the axis when finding dataMin
  10509. * and dataMax, and from the series.translate method.
  10510. *
  10511. * @ignore
  10512. * @function Highcharts.Series#init
  10513. */
  10514. Series.prototype.init = function () {
  10515. // Call base method
  10516. seriesInit.apply(this, arguments);
  10517. // Set comparison mode
  10518. this.initCompare(this.options.compare);
  10519. };
  10520. /**
  10521. * Highstock only. Set the
  10522. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  10523. * mode of the series after render time. In most cases it is more useful running
  10524. * {@link Axis#setCompare} on the X axis to update all its series.
  10525. *
  10526. * @function Highcharts.Series#setCompare
  10527. *
  10528. * @param {string} [compare]
  10529. * Can be one of `null` (default), `"percent"` or `"value"`.
  10530. */
  10531. Series.prototype.setCompare = function (compare) {
  10532. this.initCompare(compare);
  10533. // Survive to export, #5485
  10534. this.userOptions.compare = compare;
  10535. };
  10536. /**
  10537. * @ignore
  10538. * @function Highcharts.Series#initCompare
  10539. *
  10540. * @param {string} [compare]
  10541. * Can be one of `null` (default), `"percent"` or `"value"`.
  10542. */
  10543. Series.prototype.initCompare = function (compare) {
  10544. // Set or unset the modifyValue method
  10545. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  10546. function (value, point) {
  10547. var compareValue = this.compareValue;
  10548. if (typeof value !== 'undefined' &&
  10549. typeof compareValue !== 'undefined') { // #2601, #5814
  10550. // Get the modified value
  10551. if (compare === 'value') {
  10552. value -= compareValue;
  10553. // Compare percent
  10554. }
  10555. else {
  10556. value = 100 * (value / compareValue) -
  10557. (this.options.compareBase === 100 ? 0 : 100);
  10558. }
  10559. // record for tooltip etc.
  10560. if (point) {
  10561. point.change = value;
  10562. }
  10563. return value;
  10564. }
  10565. return 0;
  10566. } :
  10567. null;
  10568. // Mark dirty
  10569. if (this.chart.hasRendered) {
  10570. this.isDirty = true;
  10571. }
  10572. };
  10573. /**
  10574. * Extend series.processData by finding the first y value in the plot area,
  10575. * used for comparing the following values
  10576. *
  10577. * @ignore
  10578. * @function Highcharts.Series#processData
  10579. */
  10580. Series.prototype.processData = function (force) {
  10581. var series = this,
  10582. i,
  10583. keyIndex = -1,
  10584. processedXData,
  10585. processedYData,
  10586. compareStart = series.options.compareStart === true ? 0 : 1,
  10587. length,
  10588. compareValue;
  10589. // call base method
  10590. seriesProcessData.apply(this, arguments);
  10591. if (series.xAxis && series.processedYData) { // not pies
  10592. // local variables
  10593. processedXData = series.processedXData;
  10594. processedYData = series.processedYData;
  10595. length = processedYData.length;
  10596. // For series with more than one value (range, OHLC etc), compare
  10597. // against close or the pointValKey (#4922, #3112, #9854)
  10598. if (series.pointArrayMap) {
  10599. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  10600. }
  10601. // find the first value for comparison
  10602. for (i = 0; i < length - compareStart; i++) {
  10603. compareValue = processedYData[i] && keyIndex > -1 ?
  10604. processedYData[i][keyIndex] :
  10605. processedYData[i];
  10606. if (isNumber(compareValue) &&
  10607. processedXData[i + compareStart] >=
  10608. series.xAxis.min &&
  10609. compareValue !== 0) {
  10610. series.compareValue = compareValue;
  10611. break;
  10612. }
  10613. }
  10614. }
  10615. return;
  10616. };
  10617. // Modify series extremes
  10618. addEvent(Series, 'afterGetExtremes', function (e) {
  10619. var dataExtremes = e.dataExtremes;
  10620. if (this.modifyValue && dataExtremes) {
  10621. var extremes = [
  10622. this.modifyValue(dataExtremes.dataMin),
  10623. this.modifyValue(dataExtremes.dataMax)
  10624. ];
  10625. dataExtremes.dataMin = arrayMin(extremes);
  10626. dataExtremes.dataMax = arrayMax(extremes);
  10627. }
  10628. });
  10629. /**
  10630. * Highstock only. Set the compare mode on all series belonging to an Y axis
  10631. * after render time.
  10632. *
  10633. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  10634. *
  10635. * @sample stock/members/axis-setcompare/
  10636. * Set compoare
  10637. *
  10638. * @function Highcharts.Axis#setCompare
  10639. *
  10640. * @param {string} [compare]
  10641. * The compare mode. Can be one of `null` (default), `"value"` or
  10642. * `"percent"`.
  10643. *
  10644. * @param {boolean} [redraw=true]
  10645. * Whether to redraw the chart or to wait for a later call to
  10646. * {@link Chart#redraw}.
  10647. */
  10648. Axis.prototype.setCompare = function (compare, redraw) {
  10649. if (!this.isXAxis) {
  10650. this.series.forEach(function (series) {
  10651. series.setCompare(compare);
  10652. });
  10653. if (pick(redraw, true)) {
  10654. this.chart.redraw();
  10655. }
  10656. }
  10657. };
  10658. /**
  10659. * Extend the tooltip formatter by adding support for the point.change variable
  10660. * as well as the changeDecimals option.
  10661. *
  10662. * @ignore
  10663. * @function Highcharts.Point#tooltipFormatter
  10664. *
  10665. * @param {string} pointFormat
  10666. */
  10667. Point.prototype.tooltipFormatter = function (pointFormat) {
  10668. var point = this;
  10669. var numberFormatter = point.series.chart.numberFormatter;
  10670. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  10671. return pointTooltipFormatter.apply(this, [pointFormat]);
  10672. };
  10673. /* ************************************************************************** *
  10674. * End value compare logic *
  10675. * ************************************************************************** */
  10676. // Extend the Series prototype to create a separate series clip box. This is
  10677. // related to using multiple panes, and a future pane logic should incorporate
  10678. // this feature (#2754).
  10679. addEvent(Series, 'render', function () {
  10680. var chart = this.chart,
  10681. clipHeight;
  10682. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  10683. // if the series type handles clipping in the animate method (#2975).
  10684. if (!(chart.is3d && chart.is3d()) &&
  10685. !chart.polar &&
  10686. this.xAxis &&
  10687. !this.xAxis.isRadial // Gauge, #6192
  10688. ) {
  10689. clipHeight = this.yAxis.len;
  10690. // Include xAxis line width (#8031) but only if the Y axis ends on the
  10691. // edge of the X axis (#11005).
  10692. if (this.xAxis.axisLine) {
  10693. var dist = chart.plotTop + chart.plotHeight -
  10694. this.yAxis.pos - this.yAxis.len,
  10695. lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  10696. if (dist >= 0) {
  10697. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  10698. }
  10699. }
  10700. // First render, initial clip box
  10701. if (!this.clipBox && this.isDirty && !this.isDirtyData) {
  10702. this.clipBox = merge(chart.clipBox);
  10703. this.clipBox.width = this.xAxis.len;
  10704. this.clipBox.height = clipHeight;
  10705. // On redrawing, resizing etc, update the clip rectangle
  10706. }
  10707. else if (chart[this.sharedClipKey]) {
  10708. // animate in case resize is done during initial animation
  10709. chart[this.sharedClipKey].animate({
  10710. width: this.xAxis.len,
  10711. height: clipHeight
  10712. });
  10713. // also change markers clip animation for consistency
  10714. // (marker clip rects should exist only on chart init)
  10715. if (chart[this.sharedClipKey + 'm']) {
  10716. chart[this.sharedClipKey + 'm'].animate({
  10717. width: this.xAxis.len
  10718. });
  10719. }
  10720. }
  10721. }
  10722. });
  10723. addEvent(Chart, 'update', function (e) {
  10724. var options = e.options;
  10725. // Use case: enabling scrollbar from a disabled state.
  10726. // Scrollbar needs to be initialized from a controller, Navigator in this
  10727. // case (#6615)
  10728. if ('scrollbar' in options && this.navigator) {
  10729. merge(true, this.options.scrollbar, options.scrollbar);
  10730. this.navigator.update({}, false);
  10731. delete options.scrollbar;
  10732. }
  10733. });
  10734. /* *
  10735. *
  10736. * Compatibility
  10737. *
  10738. * */
  10739. H.StockChart = H.stockChart = stockChart;
  10740. /* *
  10741. *
  10742. * Default Export
  10743. *
  10744. * */
  10745. /* *
  10746. *
  10747. * API Options
  10748. *
  10749. * */
  10750. /**
  10751. * Compare the values of the series against the first non-null, non-
  10752. * zero value in the visible range. The y axis will show percentage
  10753. * or absolute change depending on whether `compare` is set to `"percent"`
  10754. * or `"value"`. When this is applied to multiple series, it allows
  10755. * comparing the development of the series against each other. Adds
  10756. * a `change` field to every point object.
  10757. *
  10758. * @see [compareBase](#plotOptions.series.compareBase)
  10759. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  10760. *
  10761. * @sample {highstock} stock/plotoptions/series-compare-percent/
  10762. * Percent
  10763. * @sample {highstock} stock/plotoptions/series-compare-value/
  10764. * Value
  10765. *
  10766. * @type {string}
  10767. * @since 1.0.1
  10768. * @product highstock
  10769. * @apioption plotOptions.series.compare
  10770. */
  10771. /**
  10772. * Defines if comparison should start from the first point within the visible
  10773. * range or should start from the first point **before** the range.
  10774. *
  10775. * In other words, this flag determines if first point within the visible range
  10776. * will have 0% (`compareStart=true`) or should have been already calculated
  10777. * according to the previous point (`compareStart=false`).
  10778. *
  10779. * @sample {highstock} stock/plotoptions/series-comparestart/
  10780. * Calculate compare within visible range
  10781. *
  10782. * @type {boolean}
  10783. * @default false
  10784. * @since 6.0.0
  10785. * @product highstock
  10786. * @apioption plotOptions.series.compareStart
  10787. */
  10788. /**
  10789. * When [compare](#plotOptions.series.compare) is `percent`, this option
  10790. * dictates whether to use 0 or 100 as the base of comparison.
  10791. *
  10792. * @sample {highstock} stock/plotoptions/series-comparebase/
  10793. * Compare base is 100
  10794. *
  10795. * @type {number}
  10796. * @default 0
  10797. * @since 5.0.6
  10798. * @product highstock
  10799. * @validvalue [0, 100]
  10800. * @apioption plotOptions.series.compareBase
  10801. */
  10802. ''; // keeps doclets above in transpiled file
  10803. return stockChart;
  10804. });
  10805. _registerModule(_modules, 'masters/modules/stock.src.js', [], function () {
  10806. });
  10807. }));