stock.src.js 497 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197
  1. /**
  2. * @license Highstock JS v9.1.0 (2021-05-04)
  3. *
  4. * Highcharts Stock as a plugin for Highcharts
  5. *
  6. * (c) 2010-2021 Torstein Honsi
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/stock', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * (c) 2010-2021 Torstein Honsi
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * */
  41. var isTouchDevice = H.isTouchDevice;
  42. var addEvent = U.addEvent,
  43. correctFloat = U.correctFloat,
  44. defined = U.defined,
  45. isNumber = U.isNumber,
  46. pick = U.pick;
  47. /* eslint-disable valid-jsdoc */
  48. /**
  49. * @private
  50. * @class
  51. */
  52. var NavigatorAxisAdditions = /** @class */ (function () {
  53. /* *
  54. *
  55. * Constructors
  56. *
  57. * */
  58. function NavigatorAxisAdditions(axis) {
  59. this.axis = axis;
  60. }
  61. /* *
  62. *
  63. * Functions
  64. *
  65. * */
  66. /**
  67. * @private
  68. */
  69. NavigatorAxisAdditions.prototype.destroy = function () {
  70. this.axis = void 0;
  71. };
  72. /**
  73. * Add logic to normalize the zoomed range in order to preserve the pressed
  74. * state of range selector buttons
  75. *
  76. * @private
  77. * @function Highcharts.Axis#toFixedRange
  78. * @param {number} [pxMin]
  79. * @param {number} [pxMax]
  80. * @param {number} [fixedMin]
  81. * @param {number} [fixedMax]
  82. * @return {*}
  83. */
  84. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  85. var navigator = this;
  86. var axis = navigator.axis;
  87. var chart = axis.chart;
  88. var fixedRange = chart && chart.fixedRange,
  89. halfPointRange = (axis.pointRange || 0) / 2,
  90. newMin = pick(fixedMin,
  91. axis.translate(pxMin,
  92. true, !axis.horiz)),
  93. newMax = pick(fixedMax,
  94. axis.translate(pxMax,
  95. true, !axis.horiz)),
  96. changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  97. // Add/remove half point range to/from the extremes (#1172)
  98. if (!defined(fixedMin)) {
  99. newMin = correctFloat(newMin + halfPointRange);
  100. }
  101. if (!defined(fixedMax)) {
  102. newMax = correctFloat(newMax - halfPointRange);
  103. }
  104. // If the difference between the fixed range and the actual requested
  105. // range is too great, the user is dragging across an ordinal gap, and
  106. // we need to release the range selector button.
  107. if (changeRatio > 0.7 && changeRatio < 1.3) {
  108. if (fixedMax) {
  109. newMin = newMax - fixedRange;
  110. }
  111. else {
  112. newMax = newMin + fixedRange;
  113. }
  114. }
  115. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  116. newMin = newMax = void 0;
  117. }
  118. return {
  119. min: newMin,
  120. max: newMax
  121. };
  122. };
  123. return NavigatorAxisAdditions;
  124. }());
  125. /**
  126. * @private
  127. * @class
  128. */
  129. var NavigatorAxis = /** @class */ (function () {
  130. function NavigatorAxis() {
  131. }
  132. /* *
  133. *
  134. * Static Functions
  135. *
  136. * */
  137. /**
  138. * @private
  139. */
  140. NavigatorAxis.compose = function (AxisClass) {
  141. AxisClass.keepProps.push('navigatorAxis');
  142. /* eslint-disable no-invalid-this */
  143. addEvent(AxisClass, 'init', function () {
  144. var axis = this;
  145. if (!axis.navigatorAxis) {
  146. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  147. }
  148. });
  149. // For Stock charts, override selection zooming with some special
  150. // features because X axis zooming is already allowed by the Navigator
  151. // and Range selector.
  152. addEvent(AxisClass, 'zoom', function (e) {
  153. var axis = this;
  154. var chart = axis.chart;
  155. var chartOptions = chart.options;
  156. var navigator = chartOptions.navigator;
  157. var navigatorAxis = axis.navigatorAxis;
  158. var pinchType = chartOptions.chart.pinchType;
  159. var rangeSelector = chartOptions.rangeSelector;
  160. var zoomType = chartOptions.chart.zoomType;
  161. var previousZoom;
  162. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  163. (rangeSelector && rangeSelector.enabled))) {
  164. // For y only zooming, ignore the X axis completely
  165. if (zoomType === 'y') {
  166. e.zoomed = false;
  167. // For xy zooming, record the state of the zoom before zoom
  168. // selection, then when the reset button is pressed, revert to
  169. // this state. This should apply only if the chart is
  170. // initialized with a range (#6612), otherwise zoom all the way
  171. // out.
  172. }
  173. else if (((!isTouchDevice && zoomType === 'xy') ||
  174. (isTouchDevice && pinchType === 'xy')) &&
  175. axis.options.range) {
  176. previousZoom = navigatorAxis.previousZoom;
  177. if (defined(e.newMin)) {
  178. navigatorAxis.previousZoom = [axis.min, axis.max];
  179. }
  180. else if (previousZoom) {
  181. e.newMin = previousZoom[0];
  182. e.newMax = previousZoom[1];
  183. navigatorAxis.previousZoom = void 0;
  184. }
  185. }
  186. }
  187. if (typeof e.zoomed !== 'undefined') {
  188. e.preventDefault();
  189. }
  190. });
  191. /* eslint-enable no-invalid-this */
  192. };
  193. /* *
  194. *
  195. * Static Properties
  196. *
  197. * */
  198. /**
  199. * @private
  200. */
  201. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  202. return NavigatorAxis;
  203. }());
  204. return NavigatorAxis;
  205. });
  206. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {
  207. /* *
  208. *
  209. * (c) 2010-2021 Torstein Honsi
  210. *
  211. * License: www.highcharts.com/license
  212. *
  213. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  214. *
  215. * */
  216. var addEvent = U.addEvent,
  217. defined = U.defined,
  218. pick = U.pick;
  219. /* 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 (this.shouldUpdateExtremes(e.DOMType)) {
  288. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e);
  289. }
  290. else {
  291. // When live redraw is disabled, don't change extremes
  292. // Only change the position of the scollbar thumb
  293. this.setRange(this.from, this.to);
  294. }
  295. });
  296. }
  297. });
  298. // Wrap rendering axis, and update scrollbar if one is created:
  299. addEvent(AxisClass, 'afterRender', function () {
  300. var axis = this,
  301. _a = getExtremes(axis),
  302. scrollMin = _a.scrollMin,
  303. scrollMax = _a.scrollMax,
  304. scrollbar = axis.scrollbar,
  305. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  306. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  307. axisMargin = axis.options.margin || 0,
  308. offsetsIndex,
  309. from,
  310. to;
  311. if (scrollbar) {
  312. if (axis.horiz) {
  313. // Reserve space for labels/title
  314. if (!axis.opposite) {
  315. scrollbarsOffsets[1] += offset;
  316. }
  317. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  318. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  319. // Next scrollbar should reserve space for margin (if set)
  320. if (!axis.opposite) {
  321. scrollbarsOffsets[1] += axisMargin;
  322. }
  323. offsetsIndex = 1;
  324. }
  325. else {
  326. // Reserve space for labels/title
  327. if (axis.opposite) {
  328. scrollbarsOffsets[0] += offset;
  329. }
  330. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  331. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  332. // Next scrollbar should reserve space for margin (if set)
  333. if (axis.opposite) {
  334. scrollbarsOffsets[0] += axisMargin;
  335. }
  336. offsetsIndex = 0;
  337. }
  338. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  339. scrollbar.options.margin;
  340. if (isNaN(scrollMin) ||
  341. isNaN(scrollMax) ||
  342. !defined(axis.min) ||
  343. !defined(axis.max) ||
  344. axis.min === axis.max // #10733
  345. ) {
  346. // default action: when extremes are the same or there is
  347. // not extremes on the axis, but scrollbar exists, make it
  348. // full size
  349. scrollbar.setRange(0, 1);
  350. }
  351. else {
  352. from =
  353. (axis.min - scrollMin) / (scrollMax - scrollMin);
  354. to =
  355. (axis.max - scrollMin) / (scrollMax - scrollMin);
  356. if ((axis.horiz && !axis.reversed) ||
  357. (!axis.horiz && axis.reversed)) {
  358. scrollbar.setRange(from, to);
  359. }
  360. else {
  361. // inverse vertical axis
  362. scrollbar.setRange(1 - to, 1 - from);
  363. }
  364. }
  365. }
  366. });
  367. // Make space for a scrollbar:
  368. addEvent(AxisClass, 'afterGetOffset', function () {
  369. var axis = this,
  370. index = axis.horiz ? 2 : 1,
  371. scrollbar = axis.scrollbar;
  372. if (scrollbar) {
  373. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  374. axis.chart.axisOffset[index] +=
  375. scrollbar.size + scrollbar.options.margin;
  376. }
  377. });
  378. };
  379. return ScrollbarAxis;
  380. }());
  381. return ScrollbarAxis;
  382. });
  383. _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) {
  384. /* *
  385. *
  386. * (c) 2010-2021 Torstein Honsi
  387. *
  388. * License: www.highcharts.com/license
  389. *
  390. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  391. *
  392. * */
  393. var addEvent = U.addEvent,
  394. correctFloat = U.correctFloat,
  395. defined = U.defined,
  396. destroyObjectProperties = U.destroyObjectProperties,
  397. fireEvent = U.fireEvent,
  398. merge = U.merge,
  399. pick = U.pick,
  400. removeEvent = U.removeEvent;
  401. var defaultOptions = O.defaultOptions;
  402. var isTouchDevice = H.isTouchDevice;
  403. /**
  404. * When we have vertical scrollbar, rifles and arrow in buttons should be
  405. * rotated. The same method is used in Navigator's handles, to rotate them.
  406. *
  407. * @function Highcharts.swapXY
  408. *
  409. * @param {Highcharts.SVGPathArray} path
  410. * Path to be rotated.
  411. *
  412. * @param {boolean} [vertical]
  413. * If vertical scrollbar, swap x-y values.
  414. *
  415. * @return {Highcharts.SVGPathArray}
  416. * Rotated path.
  417. *
  418. * @requires modules/stock
  419. */
  420. var swapXY = H.swapXY = function (path,
  421. vertical) {
  422. if (vertical) {
  423. path.forEach(function (seg) {
  424. var len = seg.length;
  425. var temp;
  426. for (var i = 0; i < len; i += 2) {
  427. temp = seg[i + 1];
  428. if (typeof temp === 'number') {
  429. seg[i + 1] = seg[i + 2];
  430. seg[i + 2] = temp;
  431. }
  432. }
  433. });
  434. }
  435. return path;
  436. };
  437. /* eslint-disable no-invalid-this, valid-jsdoc */
  438. /**
  439. * A reusable scrollbar, internally used in Highcharts Stock's
  440. * navigator and optionally on individual axes.
  441. *
  442. * @private
  443. * @class
  444. * @name Highcharts.Scrollbar
  445. * @param {Highcharts.SVGRenderer} renderer
  446. * @param {Highcharts.ScrollbarOptions} options
  447. * @param {Highcharts.Chart} chart
  448. */
  449. var Scrollbar = /** @class */ (function () {
  450. /* *
  451. *
  452. * Constructors
  453. *
  454. * */
  455. function Scrollbar(renderer, options, chart) {
  456. /* *
  457. *
  458. * Properties
  459. *
  460. * */
  461. this._events = [];
  462. this.chartX = 0;
  463. this.chartY = 0;
  464. this.from = 0;
  465. this.group = void 0;
  466. this.scrollbar = void 0;
  467. this.scrollbarButtons = [];
  468. this.scrollbarGroup = void 0;
  469. this.scrollbarLeft = 0;
  470. this.scrollbarRifles = void 0;
  471. this.scrollbarStrokeWidth = 1;
  472. this.scrollbarTop = 0;
  473. this.size = 0;
  474. this.to = 0;
  475. this.track = void 0;
  476. this.trackBorderWidth = 1;
  477. this.userOptions = {};
  478. this.x = 0;
  479. this.y = 0;
  480. this.chart = chart;
  481. this.options = options;
  482. this.renderer = chart.renderer;
  483. this.init(renderer, options, chart);
  484. }
  485. /* *
  486. *
  487. * Functions
  488. *
  489. * */
  490. /**
  491. * Set up the mouse and touch events for the Scrollbar
  492. *
  493. * @private
  494. * @function Highcharts.Scrollbar#addEvents
  495. * @return {void}
  496. */
  497. Scrollbar.prototype.addEvents = function () {
  498. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  499. buttons = this.scrollbarButtons,
  500. bar = this.scrollbarGroup.element,
  501. track = this.track.element,
  502. mouseDownHandler = this.mouseDownHandler.bind(this),
  503. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  504. mouseUpHandler = this.mouseUpHandler.bind(this),
  505. _events;
  506. // Mouse events
  507. _events = [
  508. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  509. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  510. [track, 'click', this.trackClick.bind(this)],
  511. [bar, 'mousedown', mouseDownHandler],
  512. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  513. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  514. ];
  515. // Touch events
  516. if (H.hasTouch) {
  517. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  518. }
  519. // Add them all
  520. _events.forEach(function (args) {
  521. addEvent.apply(null, args);
  522. });
  523. this._events = _events;
  524. };
  525. Scrollbar.prototype.buttonToMaxClick = function (e) {
  526. var scroller = this;
  527. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  528. scroller.updatePosition(scroller.from + range, scroller.to + range);
  529. fireEvent(scroller, 'changed', {
  530. from: scroller.from,
  531. to: scroller.to,
  532. trigger: 'scrollbar',
  533. DOMEvent: e
  534. });
  535. };
  536. Scrollbar.prototype.buttonToMinClick = function (e) {
  537. var scroller = this;
  538. var range = correctFloat(scroller.to - scroller.from) *
  539. pick(scroller.options.step, 0.2);
  540. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  541. fireEvent(scroller, 'changed', {
  542. from: scroller.from,
  543. to: scroller.to,
  544. trigger: 'scrollbar',
  545. DOMEvent: e
  546. });
  547. };
  548. /**
  549. * Get normalized (0-1) cursor position over the scrollbar
  550. *
  551. * @private
  552. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  553. *
  554. * @param {*} normalizedEvent
  555. * normalized event, with chartX and chartY values
  556. *
  557. * @return {Highcharts.Dictionary<number>}
  558. * Local position {chartX, chartY}
  559. */
  560. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  561. var scroller = this,
  562. options = scroller.options,
  563. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  564. options.minWidth :
  565. 0; // minWidth distorts translation
  566. return {
  567. chartX: (normalizedEvent.chartX - scroller.x -
  568. scroller.xOffset) /
  569. (scroller.barWidth - minWidthDifference),
  570. chartY: (normalizedEvent.chartY - scroller.y -
  571. scroller.yOffset) /
  572. (scroller.barWidth - minWidthDifference)
  573. };
  574. };
  575. /**
  576. * Destroys allocated elements.
  577. *
  578. * @private
  579. * @function Highcharts.Scrollbar#destroy
  580. * @return {void}
  581. */
  582. Scrollbar.prototype.destroy = function () {
  583. var scroller = this.chart.scroller;
  584. // Disconnect events added in addEvents
  585. this.removeEvents();
  586. // Destroy properties
  587. [
  588. 'track',
  589. 'scrollbarRifles',
  590. 'scrollbar',
  591. 'scrollbarGroup',
  592. 'group'
  593. ].forEach(function (prop) {
  594. if (this[prop] && this[prop].destroy) {
  595. this[prop] = this[prop].destroy();
  596. }
  597. }, this);
  598. // #6421, chart may have more scrollbars
  599. if (scroller && this === scroller.scrollbar) {
  600. scroller.scrollbar = null;
  601. // Destroy elements in collection
  602. destroyObjectProperties(scroller.scrollbarButtons);
  603. }
  604. };
  605. /**
  606. * Draw the scrollbar buttons with arrows
  607. *
  608. * @private
  609. * @function Highcharts.Scrollbar#drawScrollbarButton
  610. * @param {number} index
  611. * 0 is left, 1 is right
  612. * @return {void}
  613. */
  614. Scrollbar.prototype.drawScrollbarButton = function (index) {
  615. var scroller = this,
  616. renderer = scroller.renderer,
  617. scrollbarButtons = scroller.scrollbarButtons,
  618. options = scroller.options,
  619. size = scroller.size,
  620. group,
  621. tempElem;
  622. group = renderer.g().add(scroller.group);
  623. scrollbarButtons.push(group);
  624. // Create a rectangle for the scrollbar button
  625. tempElem = renderer.rect()
  626. .addClass('highcharts-scrollbar-button')
  627. .add(group);
  628. // Presentational attributes
  629. if (!this.chart.styledMode) {
  630. tempElem.attr({
  631. stroke: options.buttonBorderColor,
  632. 'stroke-width': options.buttonBorderWidth,
  633. fill: options.buttonBackgroundColor
  634. });
  635. }
  636. // Place the rectangle based on the rendered stroke width
  637. tempElem.attr(tempElem.crisp({
  638. x: -0.5,
  639. y: -0.5,
  640. width: size + 1,
  641. height: size + 1,
  642. r: options.buttonBorderRadius
  643. }, tempElem.strokeWidth()));
  644. // Button arrow
  645. tempElem = renderer
  646. .path(swapXY([[
  647. 'M',
  648. size / 2 + (index ? -1 : 1),
  649. size / 2 - 3
  650. ], [
  651. 'L',
  652. size / 2 + (index ? -1 : 1),
  653. size / 2 + 3
  654. ], [
  655. 'L',
  656. size / 2 + (index ? 2 : -2),
  657. size / 2
  658. ]], options.vertical))
  659. .addClass('highcharts-scrollbar-arrow')
  660. .add(scrollbarButtons[index]);
  661. if (!this.chart.styledMode) {
  662. tempElem.attr({
  663. fill: options.buttonArrowColor
  664. });
  665. }
  666. };
  667. /**
  668. * @private
  669. * @function Highcharts.Scrollbar#init
  670. * @param {Highcharts.SVGRenderer} renderer
  671. * @param {Highcharts.ScrollbarOptions} options
  672. * @param {Highcharts.Chart} chart
  673. */
  674. Scrollbar.prototype.init = function (renderer, options, chart) {
  675. this.scrollbarButtons = [];
  676. this.renderer = renderer;
  677. this.userOptions = options;
  678. this.options = merge(Scrollbar.defaultOptions, options);
  679. this.chart = chart;
  680. // backward compatibility
  681. this.size = pick(this.options.size, this.options.height);
  682. // Init
  683. if (options.enabled) {
  684. this.render();
  685. this.addEvents();
  686. }
  687. };
  688. Scrollbar.prototype.mouseDownHandler = function (e) {
  689. var scroller = this;
  690. var normalizedEvent = scroller.chart.pointer.normalize(e),
  691. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  692. scroller.chartX = mousePosition.chartX;
  693. scroller.chartY = mousePosition.chartY;
  694. scroller.initPositions = [scroller.from, scroller.to];
  695. scroller.grabbedCenter = true;
  696. };
  697. /**
  698. * Event handler for the mouse move event.
  699. * @private
  700. */
  701. Scrollbar.prototype.mouseMoveHandler = function (e) {
  702. var scroller = this;
  703. var normalizedEvent = scroller.chart.pointer.normalize(e),
  704. options = scroller.options,
  705. direction = options.vertical ? 'chartY' : 'chartX',
  706. initPositions = scroller.initPositions || [],
  707. scrollPosition,
  708. chartPosition,
  709. change;
  710. // In iOS, a mousemove event with e.pageX === 0 is fired when
  711. // holding the finger down in the center of the scrollbar. This
  712. // should be ignored.
  713. if (scroller.grabbedCenter &&
  714. // #4696, scrollbar failed on Android
  715. (!e.touches || e.touches[0][direction] !== 0)) {
  716. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  717. scrollPosition = scroller[direction];
  718. change = chartPosition - scrollPosition;
  719. scroller.hasDragged = true;
  720. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  721. if (scroller.hasDragged) {
  722. fireEvent(scroller, 'changed', {
  723. from: scroller.from,
  724. to: scroller.to,
  725. trigger: 'scrollbar',
  726. DOMType: e.type,
  727. DOMEvent: e
  728. });
  729. }
  730. }
  731. };
  732. /**
  733. * Event handler for the mouse up event.
  734. * @private
  735. */
  736. Scrollbar.prototype.mouseUpHandler = function (e) {
  737. var scroller = this;
  738. if (scroller.hasDragged) {
  739. fireEvent(scroller, 'changed', {
  740. from: scroller.from,
  741. to: scroller.to,
  742. trigger: 'scrollbar',
  743. DOMType: e.type,
  744. DOMEvent: e
  745. });
  746. }
  747. scroller.grabbedCenter =
  748. scroller.hasDragged =
  749. scroller.chartX =
  750. scroller.chartY = null;
  751. };
  752. /**
  753. * Position the scrollbar, method called from a parent with defined
  754. * dimensions.
  755. *
  756. * @private
  757. * @function Highcharts.Scrollbar#position
  758. * @param {number} x
  759. * x-position on the chart
  760. * @param {number} y
  761. * y-position on the chart
  762. * @param {number} width
  763. * width of the scrollbar
  764. * @param {number} height
  765. * height of the scorllbar
  766. * @return {void}
  767. */
  768. Scrollbar.prototype.position = function (x, y, width, height) {
  769. var scroller = this,
  770. options = scroller.options,
  771. vertical = options.vertical,
  772. xOffset = height,
  773. yOffset = 0,
  774. method = scroller.rendered ? 'animate' : 'attr';
  775. scroller.x = x;
  776. scroller.y = y + this.trackBorderWidth;
  777. scroller.width = width; // width with buttons
  778. scroller.height = height;
  779. scroller.xOffset = xOffset;
  780. scroller.yOffset = yOffset;
  781. // If Scrollbar is a vertical type, swap options:
  782. if (vertical) {
  783. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  784. scroller.xOffset = xOffset = 0;
  785. scroller.barWidth = height - width * 2; // width without buttons
  786. scroller.x = x = x + scroller.options.margin;
  787. }
  788. else {
  789. scroller.height = scroller.xOffset = height = xOffset =
  790. scroller.size;
  791. scroller.barWidth = width - height * 2; // width without buttons
  792. scroller.y = scroller.y + scroller.options.margin;
  793. }
  794. // Set general position for a group:
  795. scroller.group[method]({
  796. translateX: x,
  797. translateY: scroller.y
  798. });
  799. // Resize background/track:
  800. scroller.track[method]({
  801. width: width,
  802. height: height
  803. });
  804. // Move right/bottom button ot it's place:
  805. scroller.scrollbarButtons[1][method]({
  806. translateX: vertical ? 0 : width - xOffset,
  807. translateY: vertical ? height - yOffset : 0
  808. });
  809. };
  810. /**
  811. * Removes the event handlers attached previously with addEvents.
  812. *
  813. * @private
  814. * @function Highcharts.Scrollbar#removeEvents
  815. * @return {void}
  816. */
  817. Scrollbar.prototype.removeEvents = function () {
  818. this._events.forEach(function (args) {
  819. removeEvent.apply(null, args);
  820. });
  821. this._events.length = 0;
  822. };
  823. /**
  824. * Render scrollbar with all required items.
  825. *
  826. * @private
  827. * @function Highcharts.Scrollbar#render
  828. */
  829. Scrollbar.prototype.render = function () {
  830. var scroller = this,
  831. renderer = scroller.renderer,
  832. options = scroller.options,
  833. size = scroller.size,
  834. styledMode = this.chart.styledMode,
  835. group;
  836. // Draw the scrollbar group
  837. scroller.group = group = renderer.g('scrollbar').attr({
  838. zIndex: options.zIndex,
  839. translateY: -99999
  840. }).add();
  841. // Draw the scrollbar track:
  842. scroller.track = renderer.rect()
  843. .addClass('highcharts-scrollbar-track')
  844. .attr({
  845. x: 0,
  846. r: options.trackBorderRadius || 0,
  847. height: size,
  848. width: size
  849. }).add(group);
  850. if (!styledMode) {
  851. scroller.track.attr({
  852. fill: options.trackBackgroundColor,
  853. stroke: options.trackBorderColor,
  854. 'stroke-width': options.trackBorderWidth
  855. });
  856. }
  857. this.trackBorderWidth = scroller.track.strokeWidth();
  858. scroller.track.attr({
  859. y: -this.trackBorderWidth % 2 / 2
  860. });
  861. // Draw the scrollbar itself
  862. scroller.scrollbarGroup = renderer.g().add(group);
  863. scroller.scrollbar = renderer.rect()
  864. .addClass('highcharts-scrollbar-thumb')
  865. .attr({
  866. height: size,
  867. width: size,
  868. r: options.barBorderRadius || 0
  869. }).add(scroller.scrollbarGroup);
  870. scroller.scrollbarRifles = renderer
  871. .path(swapXY([
  872. ['M', -3, size / 4],
  873. ['L', -3, 2 * size / 3],
  874. ['M', 0, size / 4],
  875. ['L', 0, 2 * size / 3],
  876. ['M', 3, size / 4],
  877. ['L', 3, 2 * size / 3]
  878. ], options.vertical))
  879. .addClass('highcharts-scrollbar-rifles')
  880. .add(scroller.scrollbarGroup);
  881. if (!styledMode) {
  882. scroller.scrollbar.attr({
  883. fill: options.barBackgroundColor,
  884. stroke: options.barBorderColor,
  885. 'stroke-width': options.barBorderWidth
  886. });
  887. scroller.scrollbarRifles.attr({
  888. stroke: options.rifleColor,
  889. 'stroke-width': 1
  890. });
  891. }
  892. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  893. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  894. // Draw the buttons:
  895. scroller.drawScrollbarButton(0);
  896. scroller.drawScrollbarButton(1);
  897. };
  898. /**
  899. * Set scrollbar size, with a given scale.
  900. *
  901. * @private
  902. * @function Highcharts.Scrollbar#setRange
  903. * @param {number} from
  904. * scale (0-1) where bar should start
  905. * @param {number} to
  906. * scale (0-1) where bar should end
  907. * @return {void}
  908. */
  909. Scrollbar.prototype.setRange = function (from, to) {
  910. var scroller = this,
  911. options = scroller.options,
  912. vertical = options.vertical,
  913. minWidth = options.minWidth,
  914. fullWidth = scroller.barWidth,
  915. fromPX,
  916. toPX,
  917. newPos,
  918. newSize,
  919. newRiflesPos,
  920. method = (this.rendered &&
  921. !this.hasDragged &&
  922. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  923. if (!defined(fullWidth)) {
  924. return;
  925. }
  926. from = Math.max(from, 0);
  927. fromPX = Math.ceil(fullWidth * from);
  928. toPX = fullWidth * Math.min(to, 1);
  929. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  930. // We need to recalculate position, if minWidth is used
  931. if (newSize < minWidth) {
  932. fromPX = (fullWidth - minWidth + newSize) * from;
  933. newSize = minWidth;
  934. }
  935. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  936. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  937. // Store current position:
  938. scroller.from = from;
  939. scroller.to = to;
  940. if (!vertical) {
  941. scroller.scrollbarGroup[method]({
  942. translateX: newPos
  943. });
  944. scroller.scrollbar[method]({
  945. width: newSize
  946. });
  947. scroller.scrollbarRifles[method]({
  948. translateX: newRiflesPos
  949. });
  950. scroller.scrollbarLeft = newPos;
  951. scroller.scrollbarTop = 0;
  952. }
  953. else {
  954. scroller.scrollbarGroup[method]({
  955. translateY: newPos
  956. });
  957. scroller.scrollbar[method]({
  958. height: newSize
  959. });
  960. scroller.scrollbarRifles[method]({
  961. translateY: newRiflesPos
  962. });
  963. scroller.scrollbarTop = newPos;
  964. scroller.scrollbarLeft = 0;
  965. }
  966. if (newSize <= 12) {
  967. scroller.scrollbarRifles.hide();
  968. }
  969. else {
  970. scroller.scrollbarRifles.show(true);
  971. }
  972. // Show or hide the scrollbar based on the showFull setting
  973. if (options.showFull === false) {
  974. if (from <= 0 && to >= 1) {
  975. scroller.group.hide();
  976. }
  977. else {
  978. scroller.group.show();
  979. }
  980. }
  981. scroller.rendered = true;
  982. };
  983. /**
  984. * Checks if the extremes should be updated in response to a scrollbar
  985. * change event.
  986. *
  987. * @private
  988. * @function Highcharts.Scrollbar#shouldUpdateExtremes
  989. * @param {string} eventType
  990. * @return {boolean}
  991. */
  992. Scrollbar.prototype.shouldUpdateExtremes = function (eventType) {
  993. return (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  994. // Mouseup always should change extremes
  995. eventType === 'mouseup' ||
  996. eventType === 'touchend' ||
  997. // Internal events
  998. !defined(eventType));
  999. };
  1000. Scrollbar.prototype.trackClick = function (e) {
  1001. var scroller = this;
  1002. var normalizedEvent = scroller.chart.pointer.normalize(e),
  1003. range = scroller.to - scroller.from,
  1004. top = scroller.y + scroller.scrollbarTop,
  1005. left = scroller.x + scroller.scrollbarLeft;
  1006. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  1007. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  1008. // On the top or on the left side of the track:
  1009. scroller.updatePosition(scroller.from + range, scroller.to + range);
  1010. }
  1011. else {
  1012. // On the bottom or the right side of the track:
  1013. scroller.updatePosition(scroller.from - range, scroller.to - range);
  1014. }
  1015. fireEvent(scroller, 'changed', {
  1016. from: scroller.from,
  1017. to: scroller.to,
  1018. trigger: 'scrollbar',
  1019. DOMEvent: e
  1020. });
  1021. };
  1022. /**
  1023. * Update the scrollbar with new options
  1024. *
  1025. * @private
  1026. * @function Highcharts.Scrollbar#update
  1027. * @param {Highcharts.ScrollbarOptions} options
  1028. * @return {void}
  1029. */
  1030. Scrollbar.prototype.update = function (options) {
  1031. this.destroy();
  1032. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  1033. };
  1034. /**
  1035. * Update position option in the Scrollbar, with normalized 0-1 scale
  1036. *
  1037. * @private
  1038. * @function Highcharts.Scrollbar#updatePosition
  1039. * @param {number} from
  1040. * @param {number} to
  1041. * @return {void}
  1042. */
  1043. Scrollbar.prototype.updatePosition = function (from, to) {
  1044. if (to > 1) {
  1045. from = correctFloat(1 - correctFloat(to - from));
  1046. to = 1;
  1047. }
  1048. if (from < 0) {
  1049. to = correctFloat(to - from);
  1050. from = 0;
  1051. }
  1052. this.from = from;
  1053. this.to = to;
  1054. };
  1055. /* *
  1056. *
  1057. * Static Properties
  1058. *
  1059. * */
  1060. /**
  1061. *
  1062. * The scrollbar is a means of panning over the X axis of a stock chart.
  1063. * Scrollbars can also be applied to other types of axes.
  1064. *
  1065. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  1066. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  1067. * is especially suitable for simpler cartesian charts on mobile.
  1068. *
  1069. * In styled mode, all the presentational options for the
  1070. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  1071. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  1072. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  1073. *
  1074. * @sample stock/yaxis/inverted-bar-scrollbar/
  1075. * A scrollbar on a simple bar chart
  1076. *
  1077. * @product highstock gantt
  1078. * @optionparent scrollbar
  1079. *
  1080. * @private
  1081. */
  1082. Scrollbar.defaultOptions = {
  1083. /**
  1084. * The height of the scrollbar. The height also applies to the width
  1085. * of the scroll arrows so that they are always squares. Defaults to
  1086. * 20 for touch devices and 14 for mouse devices.
  1087. *
  1088. * @sample stock/scrollbar/height/
  1089. * A 30px scrollbar
  1090. *
  1091. * @type {number}
  1092. * @default 20/14
  1093. */
  1094. height: isTouchDevice ? 20 : 14,
  1095. /**
  1096. * The border rounding radius of the bar.
  1097. *
  1098. * @sample stock/scrollbar/style/
  1099. * Scrollbar styling
  1100. */
  1101. barBorderRadius: 0,
  1102. /**
  1103. * The corner radius of the scrollbar buttons.
  1104. *
  1105. * @sample stock/scrollbar/style/
  1106. * Scrollbar styling
  1107. */
  1108. buttonBorderRadius: 0,
  1109. /**
  1110. * Enable or disable the scrollbar.
  1111. *
  1112. * @sample stock/scrollbar/enabled/
  1113. * Disable the scrollbar, only use navigator
  1114. *
  1115. * @type {boolean}
  1116. * @default true
  1117. * @apioption scrollbar.enabled
  1118. */
  1119. /**
  1120. * Whether to redraw the main chart as the scrollbar or the navigator
  1121. * zoomed window is moved. Defaults to `true` for modern browsers and
  1122. * `false` for legacy IE browsers as well as mobile devices.
  1123. *
  1124. * @sample stock/scrollbar/liveredraw
  1125. * Setting live redraw to false
  1126. *
  1127. * @type {boolean}
  1128. * @since 1.3
  1129. */
  1130. liveRedraw: void 0,
  1131. /**
  1132. * The margin between the scrollbar and its axis when the scrollbar is
  1133. * applied directly to an axis.
  1134. */
  1135. margin: 10,
  1136. /**
  1137. * The minimum width of the scrollbar.
  1138. *
  1139. * @since 1.2.5
  1140. */
  1141. minWidth: 6,
  1142. /**
  1143. * Whether to show or hide the scrollbar when the scrolled content is
  1144. * zoomed out to it full extent.
  1145. *
  1146. * @type {boolean}
  1147. * @default true
  1148. * @apioption scrollbar.showFull
  1149. */
  1150. step: 0.2,
  1151. /**
  1152. * The z index of the scrollbar group.
  1153. */
  1154. zIndex: 3,
  1155. /**
  1156. * The background color of the scrollbar itself.
  1157. *
  1158. * @sample stock/scrollbar/style/
  1159. * Scrollbar styling
  1160. *
  1161. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1162. */
  1163. barBackgroundColor: palette.neutralColor20,
  1164. /**
  1165. * The width of the bar's border.
  1166. *
  1167. * @sample stock/scrollbar/style/
  1168. * Scrollbar styling
  1169. */
  1170. barBorderWidth: 1,
  1171. /**
  1172. * The color of the scrollbar's border.
  1173. *
  1174. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1175. */
  1176. barBorderColor: palette.neutralColor20,
  1177. /**
  1178. * The color of the small arrow inside the scrollbar buttons.
  1179. *
  1180. * @sample stock/scrollbar/style/
  1181. * Scrollbar styling
  1182. *
  1183. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1184. */
  1185. buttonArrowColor: palette.neutralColor80,
  1186. /**
  1187. * The color of scrollbar buttons.
  1188. *
  1189. * @sample stock/scrollbar/style/
  1190. * Scrollbar styling
  1191. *
  1192. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1193. */
  1194. buttonBackgroundColor: palette.neutralColor10,
  1195. /**
  1196. * The color of the border of the scrollbar buttons.
  1197. *
  1198. * @sample stock/scrollbar/style/
  1199. * Scrollbar styling
  1200. *
  1201. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1202. */
  1203. buttonBorderColor: palette.neutralColor20,
  1204. /**
  1205. * The border width of the scrollbar buttons.
  1206. *
  1207. * @sample stock/scrollbar/style/
  1208. * Scrollbar styling
  1209. */
  1210. buttonBorderWidth: 1,
  1211. /**
  1212. * The color of the small rifles in the middle of the scrollbar.
  1213. *
  1214. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1215. */
  1216. rifleColor: palette.neutralColor80,
  1217. /**
  1218. * The color of the track background.
  1219. *
  1220. * @sample stock/scrollbar/style/
  1221. * Scrollbar styling
  1222. *
  1223. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1224. */
  1225. trackBackgroundColor: palette.neutralColor5,
  1226. /**
  1227. * The color of the border of the scrollbar track.
  1228. *
  1229. * @sample stock/scrollbar/style/
  1230. * Scrollbar styling
  1231. *
  1232. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1233. */
  1234. trackBorderColor: palette.neutralColor5,
  1235. /**
  1236. * The corner radius of the border of the scrollbar track.
  1237. *
  1238. * @sample stock/scrollbar/style/
  1239. * Scrollbar styling
  1240. *
  1241. * @type {number}
  1242. * @default 0
  1243. * @apioption scrollbar.trackBorderRadius
  1244. */
  1245. /**
  1246. * The width of the border of the scrollbar track.
  1247. *
  1248. * @sample stock/scrollbar/style/
  1249. * Scrollbar styling
  1250. */
  1251. trackBorderWidth: 1
  1252. };
  1253. return Scrollbar;
  1254. }());
  1255. if (!H.Scrollbar) {
  1256. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  1257. H.Scrollbar = Scrollbar;
  1258. ScrollbarAxis.compose(Axis, Scrollbar);
  1259. }
  1260. return H.Scrollbar;
  1261. });
  1262. _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) {
  1263. /* *
  1264. *
  1265. * (c) 2010-2021 Torstein Honsi
  1266. *
  1267. * License: www.highcharts.com/license
  1268. *
  1269. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1270. *
  1271. * */
  1272. var color = Color.parse;
  1273. var hasTouch = H.hasTouch,
  1274. isTouchDevice = H.isTouchDevice;
  1275. var defaultOptions = O.defaultOptions;
  1276. var seriesTypes = SeriesRegistry.seriesTypes;
  1277. var addEvent = U.addEvent,
  1278. clamp = U.clamp,
  1279. correctFloat = U.correctFloat,
  1280. defined = U.defined,
  1281. destroyObjectProperties = U.destroyObjectProperties,
  1282. erase = U.erase,
  1283. extend = U.extend,
  1284. find = U.find,
  1285. isArray = U.isArray,
  1286. isNumber = U.isNumber,
  1287. merge = U.merge,
  1288. pick = U.pick,
  1289. removeEvent = U.removeEvent,
  1290. splat = U.splat;
  1291. var defaultSeriesType,
  1292. // Finding the min or max of a set of variables where we don't know if they
  1293. // are defined, is a pattern that is repeated several places in Highcharts.
  1294. // Consider making this a global utility method.
  1295. numExt = function (extreme) {
  1296. var args = [];
  1297. for (var _i = 1; _i < arguments.length; _i++) {
  1298. args[_i - 1] = arguments[_i];
  1299. }
  1300. var numbers = [].filter.call(args,
  1301. isNumber);
  1302. if (numbers.length) {
  1303. return Math[extreme].apply(0, numbers);
  1304. }
  1305. };
  1306. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  1307. 'line' :
  1308. 'areaspline';
  1309. extend(defaultOptions, {
  1310. /**
  1311. * Maximum range which can be set using the navigator's handles.
  1312. * Opposite of [xAxis.minRange](#xAxis.minRange).
  1313. *
  1314. * @sample {highstock} stock/navigator/maxrange/
  1315. * Defined max and min range
  1316. *
  1317. * @type {number}
  1318. * @since 6.0.0
  1319. * @product highstock gantt
  1320. * @apioption xAxis.maxRange
  1321. */
  1322. /**
  1323. * The navigator is a small series below the main series, displaying
  1324. * a view of the entire data set. It provides tools to zoom in and
  1325. * out on parts of the data as well as panning across the dataset.
  1326. *
  1327. * @product highstock gantt
  1328. * @optionparent navigator
  1329. */
  1330. navigator: {
  1331. /**
  1332. * Whether the navigator and scrollbar should adapt to updated data
  1333. * in the base X axis. When loading data async, as in the demo below,
  1334. * this should be `false`. Otherwise new data will trigger navigator
  1335. * redraw, which will cause unwanted looping. In the demo below, the
  1336. * data in the navigator is set only once. On navigating, only the main
  1337. * chart content is updated.
  1338. *
  1339. * @sample {highstock} stock/demo/lazy-loading/
  1340. * Set to false with async data loading
  1341. *
  1342. * @type {boolean}
  1343. * @default true
  1344. * @apioption navigator.adaptToUpdatedData
  1345. */
  1346. /**
  1347. * An integer identifying the index to use for the base series, or a
  1348. * string representing the id of the series.
  1349. *
  1350. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  1351. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  1352. *
  1353. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  1354. *
  1355. * @deprecated
  1356. * @type {number|string}
  1357. * @default 0
  1358. * @apioption navigator.baseSeries
  1359. */
  1360. /**
  1361. * Enable or disable the navigator.
  1362. *
  1363. * @sample {highstock} stock/navigator/enabled/
  1364. * Disable the navigator
  1365. *
  1366. * @type {boolean}
  1367. * @default true
  1368. * @apioption navigator.enabled
  1369. */
  1370. /**
  1371. * When the chart is inverted, whether to draw the navigator on the
  1372. * opposite side.
  1373. *
  1374. * @type {boolean}
  1375. * @default false
  1376. * @since 5.0.8
  1377. * @apioption navigator.opposite
  1378. */
  1379. /**
  1380. * The height of the navigator.
  1381. *
  1382. * @sample {highstock} stock/navigator/height/
  1383. * A higher navigator
  1384. */
  1385. height: 40,
  1386. /**
  1387. * The distance from the nearest element, the X axis or X axis labels.
  1388. *
  1389. * @sample {highstock} stock/navigator/margin/
  1390. * A margin of 2 draws the navigator closer to the X axis labels
  1391. */
  1392. margin: 25,
  1393. /**
  1394. * Whether the mask should be inside the range marking the zoomed
  1395. * range, or outside. In Highcharts Stock 1.x it was always `false`.
  1396. *
  1397. * @sample {highstock} stock/navigator/maskinside-false/
  1398. * False, mask outside
  1399. *
  1400. * @since 2.0
  1401. */
  1402. maskInside: true,
  1403. /**
  1404. * Options for the handles for dragging the zoomed area.
  1405. *
  1406. * @sample {highstock} stock/navigator/handles/
  1407. * Colored handles
  1408. */
  1409. handles: {
  1410. /**
  1411. * Width for handles.
  1412. *
  1413. * @sample {highstock} stock/navigator/styled-handles/
  1414. * Styled handles
  1415. *
  1416. * @since 6.0.0
  1417. */
  1418. width: 7,
  1419. /**
  1420. * Height for handles.
  1421. *
  1422. * @sample {highstock} stock/navigator/styled-handles/
  1423. * Styled handles
  1424. *
  1425. * @since 6.0.0
  1426. */
  1427. height: 15,
  1428. /**
  1429. * Array to define shapes of handles. 0-index for left, 1-index for
  1430. * right.
  1431. *
  1432. * Additionally, the URL to a graphic can be given on this form:
  1433. * `url(graphic.png)`. Note that for the image to be applied to
  1434. * exported charts, its URL needs to be accessible by the export
  1435. * server.
  1436. *
  1437. * Custom callbacks for symbol path generation can also be added to
  1438. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  1439. * used by its method name, as shown in the demo.
  1440. *
  1441. * @sample {highstock} stock/navigator/styled-handles/
  1442. * Styled handles
  1443. *
  1444. * @type {Array<string>}
  1445. * @default ["navigator-handle", "navigator-handle"]
  1446. * @since 6.0.0
  1447. */
  1448. symbols: ['navigator-handle', 'navigator-handle'],
  1449. /**
  1450. * Allows to enable/disable handles.
  1451. *
  1452. * @since 6.0.0
  1453. */
  1454. enabled: true,
  1455. /**
  1456. * The width for the handle border and the stripes inside.
  1457. *
  1458. * @sample {highstock} stock/navigator/styled-handles/
  1459. * Styled handles
  1460. *
  1461. * @since 6.0.0
  1462. * @apioption navigator.handles.lineWidth
  1463. */
  1464. lineWidth: 1,
  1465. /**
  1466. * The fill for the handle.
  1467. *
  1468. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1469. */
  1470. backgroundColor: palette.neutralColor5,
  1471. /**
  1472. * The stroke for the handle border and the stripes inside.
  1473. *
  1474. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1475. */
  1476. borderColor: palette.neutralColor40
  1477. },
  1478. /**
  1479. * The color of the mask covering the areas of the navigator series
  1480. * that are currently not visible in the main series. The default
  1481. * color is bluish with an opacity of 0.3 to see the series below.
  1482. *
  1483. * @see In styled mode, the mask is styled with the
  1484. * `.highcharts-navigator-mask` and
  1485. * `.highcharts-navigator-mask-inside` classes.
  1486. *
  1487. * @sample {highstock} stock/navigator/maskfill/
  1488. * Blue, semi transparent mask
  1489. *
  1490. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1491. * @default rgba(102,133,194,0.3)
  1492. */
  1493. maskFill: color(palette.highlightColor60).setOpacity(0.3).get(),
  1494. /**
  1495. * The color of the line marking the currently zoomed area in the
  1496. * navigator.
  1497. *
  1498. * @sample {highstock} stock/navigator/outline/
  1499. * 2px blue outline
  1500. *
  1501. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1502. * @default #cccccc
  1503. */
  1504. outlineColor: palette.neutralColor20,
  1505. /**
  1506. * The width of the line marking the currently zoomed area in the
  1507. * navigator.
  1508. *
  1509. * @see In styled mode, the outline stroke width is set with the
  1510. * `.highcharts-navigator-outline` class.
  1511. *
  1512. * @sample {highstock} stock/navigator/outline/
  1513. * 2px blue outline
  1514. *
  1515. * @type {number}
  1516. */
  1517. outlineWidth: 1,
  1518. /**
  1519. * Options for the navigator series. Available options are the same
  1520. * as any series, documented at [plotOptions](#plotOptions.series)
  1521. * and [series](#series).
  1522. *
  1523. * Unless data is explicitly defined on navigator.series, the data
  1524. * is borrowed from the first series in the chart.
  1525. *
  1526. * Default series options for the navigator series are:
  1527. * ```js
  1528. * series: {
  1529. * type: 'areaspline',
  1530. * fillOpacity: 0.05,
  1531. * dataGrouping: {
  1532. * smoothed: true
  1533. * },
  1534. * lineWidth: 1,
  1535. * marker: {
  1536. * enabled: false
  1537. * }
  1538. * }
  1539. * ```
  1540. *
  1541. * @see In styled mode, the navigator series is styled with the
  1542. * `.highcharts-navigator-series` class.
  1543. *
  1544. * @sample {highstock} stock/navigator/series-data/
  1545. * Using a separate data set for the navigator
  1546. * @sample {highstock} stock/navigator/series/
  1547. * A green navigator series
  1548. *
  1549. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  1550. */
  1551. series: {
  1552. /**
  1553. * The type of the navigator series.
  1554. *
  1555. * Heads up:
  1556. * In column-type navigator, zooming is limited to at least one
  1557. * point with its `pointRange`.
  1558. *
  1559. * @sample {highstock} stock/navigator/column/
  1560. * Column type navigator
  1561. *
  1562. * @type {string}
  1563. * @default {highstock} `areaspline` if defined, otherwise `line`
  1564. * @default {gantt} gantt
  1565. */
  1566. type: defaultSeriesType,
  1567. /**
  1568. * The fill opacity of the navigator series.
  1569. */
  1570. fillOpacity: 0.05,
  1571. /**
  1572. * The pixel line width of the navigator series.
  1573. */
  1574. lineWidth: 1,
  1575. /**
  1576. * @ignore-option
  1577. */
  1578. compare: null,
  1579. /**
  1580. * Unless data is explicitly defined, the data is borrowed from the
  1581. * first series in the chart.
  1582. *
  1583. * @type {Array<number|Array<number|string|null>|object|null>}
  1584. * @product highstock
  1585. * @apioption navigator.series.data
  1586. */
  1587. /**
  1588. * Data grouping options for the navigator series.
  1589. *
  1590. * @extends plotOptions.series.dataGrouping
  1591. */
  1592. dataGrouping: {
  1593. approximation: 'average',
  1594. enabled: true,
  1595. groupPixelWidth: 2,
  1596. // Replace smoothed property by anchors, #12455.
  1597. firstAnchor: 'firstPoint',
  1598. anchor: 'middle',
  1599. lastAnchor: 'lastPoint',
  1600. // Day and week differs from plotOptions.series.dataGrouping
  1601. units: [
  1602. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  1603. ['second', [1, 2, 5, 10, 15, 30]],
  1604. ['minute', [1, 2, 5, 10, 15, 30]],
  1605. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  1606. ['day', [1, 2, 3, 4]],
  1607. ['week', [1, 2, 3]],
  1608. ['month', [1, 3, 6]],
  1609. ['year', null]
  1610. ]
  1611. },
  1612. /**
  1613. * Data label options for the navigator series. Data labels are
  1614. * disabled by default on the navigator series.
  1615. *
  1616. * @extends plotOptions.series.dataLabels
  1617. */
  1618. dataLabels: {
  1619. enabled: false,
  1620. zIndex: 2 // #1839
  1621. },
  1622. id: 'highcharts-navigator-series',
  1623. className: 'highcharts-navigator-series',
  1624. /**
  1625. * Sets the fill color of the navigator series.
  1626. *
  1627. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1628. * @apioption navigator.series.color
  1629. */
  1630. /**
  1631. * Line color for the navigator series. Allows setting the color
  1632. * while disallowing the default candlestick setting.
  1633. *
  1634. * @type {Highcharts.ColorString|null}
  1635. */
  1636. lineColor: null,
  1637. marker: {
  1638. enabled: false
  1639. },
  1640. /**
  1641. * Since Highcharts Stock v8, default value is the same as default
  1642. * `pointRange` defined for a specific type (e.g. `null` for
  1643. * column type).
  1644. *
  1645. * In Highcharts Stock version < 8, defaults to 0.
  1646. *
  1647. * @extends plotOptions.series.pointRange
  1648. * @type {number|null}
  1649. * @apioption navigator.series.pointRange
  1650. */
  1651. /**
  1652. * The threshold option. Setting it to 0 will make the default
  1653. * navigator area series draw its area from the 0 value and up.
  1654. *
  1655. * @type {number|null}
  1656. */
  1657. threshold: null
  1658. },
  1659. /**
  1660. * Options for the navigator X axis. Default series options for the
  1661. * navigator xAxis are:
  1662. * ```js
  1663. * xAxis: {
  1664. * tickWidth: 0,
  1665. * lineWidth: 0,
  1666. * gridLineWidth: 1,
  1667. * tickPixelInterval: 200,
  1668. * labels: {
  1669. * align: 'left',
  1670. * style: {
  1671. * color: '#888'
  1672. * },
  1673. * x: 3,
  1674. * y: -4
  1675. * }
  1676. * }
  1677. * ```
  1678. *
  1679. * @extends xAxis
  1680. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  1681. * showEmpty, maxRange
  1682. */
  1683. xAxis: {
  1684. /**
  1685. * Additional range on the right side of the xAxis. Works similar to
  1686. * xAxis.maxPadding, but value is set in milliseconds.
  1687. * Can be set for both, main xAxis and navigator's xAxis.
  1688. *
  1689. * @since 6.0.0
  1690. */
  1691. overscroll: 0,
  1692. className: 'highcharts-navigator-xaxis',
  1693. tickLength: 0,
  1694. lineWidth: 0,
  1695. gridLineColor: palette.neutralColor10,
  1696. gridLineWidth: 1,
  1697. tickPixelInterval: 200,
  1698. labels: {
  1699. align: 'left',
  1700. /**
  1701. * @type {Highcharts.CSSObject}
  1702. */
  1703. style: {
  1704. /** @ignore */
  1705. color: palette.neutralColor40
  1706. },
  1707. x: 3,
  1708. y: -4
  1709. },
  1710. crosshair: false
  1711. },
  1712. /**
  1713. * Options for the navigator Y axis. Default series options for the
  1714. * navigator yAxis are:
  1715. * ```js
  1716. * yAxis: {
  1717. * gridLineWidth: 0,
  1718. * startOnTick: false,
  1719. * endOnTick: false,
  1720. * minPadding: 0.1,
  1721. * maxPadding: 0.1,
  1722. * labels: {
  1723. * enabled: false
  1724. * },
  1725. * title: {
  1726. * text: null
  1727. * },
  1728. * tickWidth: 0
  1729. * }
  1730. * ```
  1731. *
  1732. * @extends yAxis
  1733. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  1734. * showEmpty, scrollbar, top, units, maxRange, minLength,
  1735. * maxLength, resize
  1736. */
  1737. yAxis: {
  1738. className: 'highcharts-navigator-yaxis',
  1739. gridLineWidth: 0,
  1740. startOnTick: false,
  1741. endOnTick: false,
  1742. minPadding: 0.1,
  1743. maxPadding: 0.1,
  1744. labels: {
  1745. enabled: false
  1746. },
  1747. crosshair: false,
  1748. title: {
  1749. text: null
  1750. },
  1751. tickLength: 0,
  1752. tickWidth: 0
  1753. }
  1754. }
  1755. });
  1756. /* eslint-disable no-invalid-this, valid-jsdoc */
  1757. /**
  1758. * Draw one of the handles on the side of the zoomed range in the navigator
  1759. *
  1760. * @private
  1761. * @function Highcharts.Renderer#symbols.navigator-handle
  1762. * @param {number} x
  1763. * @param {number} y
  1764. * @param {number} w
  1765. * @param {number} h
  1766. * @param {Highcharts.NavigatorHandlesOptions} options
  1767. * @return {Highcharts.SVGPathArray}
  1768. * Path to be used in a handle
  1769. */
  1770. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  1771. var halfWidth = (options && options.width || 0) / 2,
  1772. markerPosition = Math.round(halfWidth / 3) + 0.5,
  1773. height = options && options.height || 0;
  1774. return [
  1775. ['M', -halfWidth - 1, 0.5],
  1776. ['L', halfWidth, 0.5],
  1777. ['L', halfWidth, height + 0.5],
  1778. ['L', -halfWidth - 1, height + 0.5],
  1779. ['L', -halfWidth - 1, 0.5],
  1780. ['M', -markerPosition, 4],
  1781. ['L', -markerPosition, height - 3],
  1782. ['M', markerPosition - 1, 4],
  1783. ['L', markerPosition - 1, height - 3]
  1784. ];
  1785. };
  1786. /**
  1787. * The Navigator class
  1788. *
  1789. * @private
  1790. * @class
  1791. * @name Highcharts.Navigator
  1792. *
  1793. * @param {Highcharts.Chart} chart
  1794. * Chart object
  1795. */
  1796. var Navigator = /** @class */ (function () {
  1797. function Navigator(chart) {
  1798. this.baseSeries = void 0;
  1799. this.chart = void 0;
  1800. this.handles = void 0;
  1801. this.height = void 0;
  1802. this.left = void 0;
  1803. this.navigatorEnabled = void 0;
  1804. this.navigatorGroup = void 0;
  1805. this.navigatorOptions = void 0;
  1806. this.navigatorSeries = void 0;
  1807. this.navigatorSize = void 0;
  1808. this.opposite = void 0;
  1809. this.outline = void 0;
  1810. this.outlineHeight = void 0;
  1811. this.range = void 0;
  1812. this.rendered = void 0;
  1813. this.shades = void 0;
  1814. this.size = void 0;
  1815. this.top = void 0;
  1816. this.xAxis = void 0;
  1817. this.yAxis = void 0;
  1818. this.zoomedMax = void 0;
  1819. this.zoomedMin = void 0;
  1820. this.init(chart);
  1821. }
  1822. /**
  1823. * Draw one of the handles on the side of the zoomed range in the navigator
  1824. *
  1825. * @private
  1826. * @function Highcharts.Navigator#drawHandle
  1827. *
  1828. * @param {number} x
  1829. * The x center for the handle
  1830. *
  1831. * @param {number} index
  1832. * 0 for left and 1 for right
  1833. *
  1834. * @param {boolean|undefined} inverted
  1835. * flag for chart.inverted
  1836. *
  1837. * @param {string} verb
  1838. * use 'animate' or 'attr'
  1839. */
  1840. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  1841. var navigator = this,
  1842. height = navigator.navigatorOptions.handles.height;
  1843. // Place it
  1844. navigator.handles[index][verb](inverted ? {
  1845. translateX: Math.round(navigator.left + navigator.height / 2),
  1846. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  1847. } : {
  1848. translateX: Math.round(navigator.left + parseInt(x, 10)),
  1849. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  1850. });
  1851. };
  1852. /**
  1853. * Render outline around the zoomed range
  1854. *
  1855. * @private
  1856. * @function Highcharts.Navigator#drawOutline
  1857. *
  1858. * @param {number} zoomedMin
  1859. * in pixels position where zoomed range starts
  1860. *
  1861. * @param {number} zoomedMax
  1862. * in pixels position where zoomed range ends
  1863. *
  1864. * @param {boolean|undefined} inverted
  1865. * flag if chart is inverted
  1866. *
  1867. * @param {string} verb
  1868. * use 'animate' or 'attr'
  1869. */
  1870. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  1871. var navigator = this,
  1872. maskInside = navigator.navigatorOptions.maskInside,
  1873. outlineWidth = navigator.outline.strokeWidth(),
  1874. halfOutline = outlineWidth / 2,
  1875. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  1876. outlineHeight = navigator.outlineHeight,
  1877. scrollbarHeight = navigator.scrollbarHeight || 0,
  1878. navigatorSize = navigator.size,
  1879. left = navigator.left - scrollbarHeight,
  1880. navigatorTop = navigator.top,
  1881. verticalMin,
  1882. path;
  1883. if (inverted) {
  1884. left -= halfOutline;
  1885. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  1886. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  1887. path = [
  1888. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  1889. ['L', left + outlineHeight, verticalMin],
  1890. ['L', left, verticalMin],
  1891. ['L', left, zoomedMax],
  1892. ['L', left + outlineHeight, zoomedMax],
  1893. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  1894. ];
  1895. if (maskInside) {
  1896. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  1897. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  1898. );
  1899. }
  1900. }
  1901. else {
  1902. zoomedMin += left + scrollbarHeight - outlineCorrection;
  1903. zoomedMax += left + scrollbarHeight - outlineCorrection;
  1904. navigatorTop += halfOutline;
  1905. path = [
  1906. ['M', left, navigatorTop],
  1907. ['L', zoomedMin, navigatorTop],
  1908. ['L', zoomedMin, navigatorTop + outlineHeight],
  1909. ['L', zoomedMax, navigatorTop + outlineHeight],
  1910. ['L', zoomedMax, navigatorTop],
  1911. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  1912. ];
  1913. if (maskInside) {
  1914. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  1915. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  1916. );
  1917. }
  1918. }
  1919. navigator.outline[verb]({
  1920. d: path
  1921. });
  1922. };
  1923. /**
  1924. * Render outline around the zoomed range
  1925. *
  1926. * @private
  1927. * @function Highcharts.Navigator#drawMasks
  1928. *
  1929. * @param {number} zoomedMin
  1930. * in pixels position where zoomed range starts
  1931. *
  1932. * @param {number} zoomedMax
  1933. * in pixels position where zoomed range ends
  1934. *
  1935. * @param {boolean|undefined} inverted
  1936. * flag if chart is inverted
  1937. *
  1938. * @param {string} verb
  1939. * use 'animate' or 'attr'
  1940. */
  1941. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  1942. var navigator = this,
  1943. left = navigator.left,
  1944. top = navigator.top,
  1945. navigatorHeight = navigator.height,
  1946. height,
  1947. width,
  1948. x,
  1949. y;
  1950. // Determine rectangle position & size
  1951. // According to (non)inverted position:
  1952. if (inverted) {
  1953. x = [left, left, left];
  1954. y = [top, top + zoomedMin, top + zoomedMax];
  1955. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  1956. height = [
  1957. zoomedMin,
  1958. zoomedMax - zoomedMin,
  1959. navigator.size - zoomedMax
  1960. ];
  1961. }
  1962. else {
  1963. x = [left, left + zoomedMin, left + zoomedMax];
  1964. y = [top, top, top];
  1965. width = [
  1966. zoomedMin,
  1967. zoomedMax - zoomedMin,
  1968. navigator.size - zoomedMax
  1969. ];
  1970. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  1971. }
  1972. navigator.shades.forEach(function (shade, i) {
  1973. shade[verb]({
  1974. x: x[i],
  1975. y: y[i],
  1976. width: width[i],
  1977. height: height[i]
  1978. });
  1979. });
  1980. };
  1981. /**
  1982. * Generate DOM elements for a navigator:
  1983. *
  1984. * - main navigator group
  1985. *
  1986. * - all shades
  1987. *
  1988. * - outline
  1989. *
  1990. * - handles
  1991. *
  1992. * @private
  1993. * @function Highcharts.Navigator#renderElements
  1994. */
  1995. Navigator.prototype.renderElements = function () {
  1996. var navigator = this,
  1997. navigatorOptions = navigator.navigatorOptions,
  1998. maskInside = navigatorOptions.maskInside,
  1999. chart = navigator.chart,
  2000. inverted = chart.inverted,
  2001. renderer = chart.renderer,
  2002. navigatorGroup,
  2003. mouseCursor = {
  2004. cursor: inverted ? 'ns-resize' : 'ew-resize'
  2005. };
  2006. // Create the main navigator group
  2007. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  2008. .attr({
  2009. zIndex: 8,
  2010. visibility: 'hidden'
  2011. })
  2012. .add();
  2013. // Create masks, each mask will get events and fill:
  2014. [
  2015. !maskInside,
  2016. maskInside,
  2017. !maskInside
  2018. ].forEach(function (hasMask, index) {
  2019. navigator.shades[index] = renderer.rect()
  2020. .addClass('highcharts-navigator-mask' +
  2021. (index === 1 ? '-inside' : '-outside'))
  2022. .add(navigatorGroup);
  2023. if (!chart.styledMode) {
  2024. navigator.shades[index]
  2025. .attr({
  2026. fill: hasMask ?
  2027. navigatorOptions.maskFill :
  2028. 'rgba(0,0,0,0)'
  2029. })
  2030. .css((index === 1) && mouseCursor);
  2031. }
  2032. });
  2033. // Create the outline:
  2034. navigator.outline = renderer.path()
  2035. .addClass('highcharts-navigator-outline')
  2036. .add(navigatorGroup);
  2037. if (!chart.styledMode) {
  2038. navigator.outline.attr({
  2039. 'stroke-width': navigatorOptions.outlineWidth,
  2040. stroke: navigatorOptions.outlineColor
  2041. });
  2042. }
  2043. // Create the handlers:
  2044. if (navigatorOptions.handles.enabled) {
  2045. [0, 1].forEach(function (index) {
  2046. navigatorOptions.handles.inverted = chart.inverted;
  2047. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  2048. // zIndex = 6 for right handle, 7 for left.
  2049. // Can't be 10, because of the tooltip in inverted chart #2908
  2050. navigator.handles[index].attr({ zIndex: 7 - index })
  2051. .addClass('highcharts-navigator-handle ' +
  2052. 'highcharts-navigator-handle-' +
  2053. ['left', 'right'][index]).add(navigatorGroup);
  2054. if (!chart.styledMode) {
  2055. var handlesOptions = navigatorOptions.handles;
  2056. navigator.handles[index]
  2057. .attr({
  2058. fill: handlesOptions.backgroundColor,
  2059. stroke: handlesOptions.borderColor,
  2060. 'stroke-width': handlesOptions.lineWidth
  2061. })
  2062. .css(mouseCursor);
  2063. }
  2064. });
  2065. }
  2066. };
  2067. /**
  2068. * Update navigator
  2069. *
  2070. * @private
  2071. * @function Highcharts.Navigator#update
  2072. *
  2073. * @param {Highcharts.NavigatorOptions} options
  2074. * Options to merge in when updating navigator
  2075. */
  2076. Navigator.prototype.update = function (options) {
  2077. // Remove references to old navigator series in base series
  2078. (this.series || []).forEach(function (series) {
  2079. if (series.baseSeries) {
  2080. delete series.baseSeries.navigatorSeries;
  2081. }
  2082. });
  2083. // Destroy and rebuild navigator
  2084. this.destroy();
  2085. var chartOptions = this.chart.options;
  2086. merge(true, chartOptions.navigator, this.options, options);
  2087. this.init(this.chart);
  2088. };
  2089. /**
  2090. * Render the navigator
  2091. *
  2092. * @private
  2093. * @function Highcharts.Navigator#render
  2094. * @param {number} min
  2095. * X axis value minimum
  2096. * @param {number} max
  2097. * X axis value maximum
  2098. * @param {number} [pxMin]
  2099. * Pixel value minimum
  2100. * @param {number} [pxMax]
  2101. * Pixel value maximum
  2102. * @return {void}
  2103. */
  2104. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  2105. var navigator = this,
  2106. chart = navigator.chart,
  2107. navigatorWidth,
  2108. scrollbarLeft,
  2109. scrollbarTop,
  2110. scrollbarHeight = navigator.scrollbarHeight,
  2111. navigatorSize,
  2112. xAxis = navigator.xAxis,
  2113. pointRange = xAxis.pointRange || 0,
  2114. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  2115. navigatorEnabled = navigator.navigatorEnabled,
  2116. zoomedMin,
  2117. zoomedMax,
  2118. rendered = navigator.rendered,
  2119. inverted = chart.inverted,
  2120. verb,
  2121. newMin,
  2122. newMax,
  2123. currentRange,
  2124. minRange = chart.xAxis[0].minRange,
  2125. maxRange = chart.xAxis[0].options.maxRange;
  2126. // Don't redraw while moving the handles (#4703).
  2127. if (this.hasDragged && !defined(pxMin)) {
  2128. return;
  2129. }
  2130. min = correctFloat(min - pointRange / 2);
  2131. max = correctFloat(max + pointRange / 2);
  2132. // Don't render the navigator until we have data (#486, #4202, #5172).
  2133. if (!isNumber(min) || !isNumber(max)) {
  2134. // However, if navigator was already rendered, we may need to resize
  2135. // it. For example hidden series, but visible navigator (#6022).
  2136. if (rendered) {
  2137. pxMin = 0;
  2138. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  2139. }
  2140. else {
  2141. return;
  2142. }
  2143. }
  2144. navigator.left = pick(xAxis.left,
  2145. // in case of scrollbar only, without navigator
  2146. chart.plotLeft + scrollbarHeight +
  2147. (inverted ? chart.plotWidth : 0));
  2148. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  2149. 2 * scrollbarHeight);
  2150. if (inverted) {
  2151. navigatorWidth = scrollbarHeight;
  2152. }
  2153. else {
  2154. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  2155. }
  2156. // Get the pixel position of the handles
  2157. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  2158. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  2159. // Verify (#1851, #2238)
  2160. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  2161. pxMin = 0;
  2162. pxMax = navigatorWidth;
  2163. }
  2164. // Are we below the minRange? (#2618, #6191)
  2165. newMin = xAxis.toValue(pxMin, true);
  2166. newMax = xAxis.toValue(pxMax, true);
  2167. currentRange = Math.abs(correctFloat(newMax - newMin));
  2168. if (currentRange < minRange) {
  2169. if (this.grabbedLeft) {
  2170. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  2171. }
  2172. else if (this.grabbedRight) {
  2173. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  2174. }
  2175. }
  2176. else if (defined(maxRange) &&
  2177. correctFloat(currentRange - pointRange) > maxRange) {
  2178. if (this.grabbedLeft) {
  2179. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  2180. }
  2181. else if (this.grabbedRight) {
  2182. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  2183. }
  2184. }
  2185. // Handles are allowed to cross, but never exceed the plot area
  2186. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  2187. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  2188. navigator.zoomedMax - navigator.fixedWidth :
  2189. Math.min(pxMin, pxMax), 0, zoomedMax);
  2190. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  2191. zoomedMax = Math.round(navigator.zoomedMax);
  2192. zoomedMin = Math.round(navigator.zoomedMin);
  2193. if (navigatorEnabled) {
  2194. navigator.navigatorGroup.attr({
  2195. visibility: 'visible'
  2196. });
  2197. // Place elements
  2198. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  2199. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2200. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2201. if (navigator.navigatorOptions.handles.enabled) {
  2202. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2203. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2204. }
  2205. }
  2206. if (navigator.scrollbar) {
  2207. if (inverted) {
  2208. scrollbarTop = navigator.top - scrollbarHeight;
  2209. scrollbarLeft = navigator.left - scrollbarHeight +
  2210. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  2211. // Multiple axes has offsets:
  2212. (scrollbarXAxis.titleOffset || 0) +
  2213. // Self margin from the axis.title
  2214. scrollbarXAxis.axisTitleMargin);
  2215. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  2216. }
  2217. else {
  2218. scrollbarTop = navigator.top + (navigatorEnabled ?
  2219. navigator.height :
  2220. -scrollbarHeight);
  2221. scrollbarLeft = navigator.left - scrollbarHeight;
  2222. }
  2223. // Reposition scrollbar
  2224. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  2225. // Keep scale 0-1
  2226. navigator.scrollbar.setRange(
  2227. // Use real value, not rounded because range can be very small
  2228. // (#1716)
  2229. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  2230. }
  2231. navigator.rendered = true;
  2232. };
  2233. /**
  2234. * Set up the mouse and touch events for the navigator
  2235. *
  2236. * @private
  2237. * @function Highcharts.Navigator#addMouseEvents
  2238. */
  2239. Navigator.prototype.addMouseEvents = function () {
  2240. var navigator = this,
  2241. chart = navigator.chart,
  2242. container = chart.container,
  2243. eventsToUnbind = [],
  2244. mouseMoveHandler,
  2245. mouseUpHandler;
  2246. /**
  2247. * Create mouse events' handlers.
  2248. * Make them as separate functions to enable wrapping them:
  2249. */
  2250. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  2251. navigator.onMouseMove(e);
  2252. };
  2253. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  2254. navigator.onMouseUp(e);
  2255. };
  2256. // Add shades and handles mousedown events
  2257. eventsToUnbind = navigator.getPartsEvents('mousedown');
  2258. // Add mouse move and mouseup events. These are bind to doc/container,
  2259. // because Navigator.grabbedSomething flags are stored in mousedown
  2260. // events
  2261. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  2262. // Touch events
  2263. if (hasTouch) {
  2264. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  2265. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  2266. }
  2267. navigator.eventsToUnbind = eventsToUnbind;
  2268. // Data events
  2269. if (navigator.series && navigator.series[0]) {
  2270. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  2271. chart.navigator.modifyNavigatorAxisExtremes();
  2272. }));
  2273. }
  2274. };
  2275. /**
  2276. * Generate events for handles and masks
  2277. *
  2278. * @private
  2279. * @function Highcharts.Navigator#getPartsEvents
  2280. *
  2281. * @param {string} eventName
  2282. * Event name handler, 'mousedown' or 'touchstart'
  2283. *
  2284. * @return {Array<Function>}
  2285. * An array of functions to remove navigator functions from the
  2286. * events again.
  2287. */
  2288. Navigator.prototype.getPartsEvents = function (eventName) {
  2289. var navigator = this,
  2290. events = [];
  2291. ['shades', 'handles'].forEach(function (name) {
  2292. navigator[name].forEach(function (navigatorItem, index) {
  2293. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  2294. navigator[name + 'Mousedown'](e, index);
  2295. }));
  2296. });
  2297. });
  2298. return events;
  2299. };
  2300. /**
  2301. * Mousedown on a shaded mask, either:
  2302. *
  2303. * - will be stored for future drag&drop
  2304. *
  2305. * - will directly shift to a new range
  2306. *
  2307. * @private
  2308. * @function Highcharts.Navigator#shadesMousedown
  2309. *
  2310. * @param {Highcharts.PointerEventObject} e
  2311. * Mouse event
  2312. *
  2313. * @param {number} index
  2314. * Index of a mask in Navigator.shades array
  2315. */
  2316. Navigator.prototype.shadesMousedown = function (e, index) {
  2317. e = this.chart.pointer.normalize(e);
  2318. var navigator = this,
  2319. chart = navigator.chart,
  2320. xAxis = navigator.xAxis,
  2321. zoomedMin = navigator.zoomedMin,
  2322. navigatorPosition = navigator.left,
  2323. navigatorSize = navigator.size,
  2324. range = navigator.range,
  2325. chartX = e.chartX,
  2326. fixedMax,
  2327. fixedMin,
  2328. ext,
  2329. left;
  2330. // For inverted chart, swap some options:
  2331. if (chart.inverted) {
  2332. chartX = e.chartY;
  2333. navigatorPosition = navigator.top;
  2334. }
  2335. if (index === 1) {
  2336. // Store information for drag&drop
  2337. navigator.grabbedCenter = chartX;
  2338. navigator.fixedWidth = range;
  2339. navigator.dragOffset = chartX - zoomedMin;
  2340. }
  2341. else {
  2342. // Shift the range by clicking on shaded areas
  2343. left = chartX - navigatorPosition - range / 2;
  2344. if (index === 0) {
  2345. left = Math.max(0, left);
  2346. }
  2347. else if (index === 2 && left + range >= navigatorSize) {
  2348. left = navigatorSize - range;
  2349. if (navigator.reversedExtremes) {
  2350. // #7713
  2351. left -= range;
  2352. fixedMin = navigator.getUnionExtremes().dataMin;
  2353. }
  2354. else {
  2355. // #2293, #3543
  2356. fixedMax = navigator.getUnionExtremes().dataMax;
  2357. }
  2358. }
  2359. if (left !== zoomedMin) { // it has actually moved
  2360. navigator.fixedWidth = range; // #1370
  2361. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  2362. if (defined(ext.min)) { // #7411
  2363. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  2364. { trigger: 'navigator' });
  2365. }
  2366. }
  2367. }
  2368. };
  2369. /**
  2370. * Mousedown on a handle mask.
  2371. * Will store necessary information for drag&drop.
  2372. *
  2373. * @private
  2374. * @function Highcharts.Navigator#handlesMousedown
  2375. * @param {Highcharts.PointerEventObject} e
  2376. * Mouse event
  2377. * @param {number} index
  2378. * Index of a handle in Navigator.handles array
  2379. * @return {void}
  2380. */
  2381. Navigator.prototype.handlesMousedown = function (e, index) {
  2382. e = this.chart.pointer.normalize(e);
  2383. var navigator = this,
  2384. chart = navigator.chart,
  2385. baseXAxis = chart.xAxis[0],
  2386. // For reversed axes, min and max are changed,
  2387. // so the other extreme should be stored
  2388. reverse = navigator.reversedExtremes;
  2389. if (index === 0) {
  2390. // Grab the left handle
  2391. navigator.grabbedLeft = true;
  2392. navigator.otherHandlePos = navigator.zoomedMax;
  2393. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  2394. }
  2395. else {
  2396. // Grab the right handle
  2397. navigator.grabbedRight = true;
  2398. navigator.otherHandlePos = navigator.zoomedMin;
  2399. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  2400. }
  2401. chart.fixedRange = null;
  2402. };
  2403. /**
  2404. * Mouse move event based on x/y mouse position.
  2405. *
  2406. * @private
  2407. * @function Highcharts.Navigator#onMouseMove
  2408. *
  2409. * @param {Highcharts.PointerEventObject} e
  2410. * Mouse event
  2411. */
  2412. Navigator.prototype.onMouseMove = function (e) {
  2413. var navigator = this,
  2414. chart = navigator.chart,
  2415. left = navigator.left,
  2416. navigatorSize = navigator.navigatorSize,
  2417. range = navigator.range,
  2418. dragOffset = navigator.dragOffset,
  2419. inverted = chart.inverted,
  2420. chartX;
  2421. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  2422. // the finger down in the center of the scrollbar. This should be
  2423. // ignored.
  2424. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  2425. e = chart.pointer.normalize(e);
  2426. chartX = e.chartX;
  2427. // Swap some options for inverted chart
  2428. if (inverted) {
  2429. left = navigator.top;
  2430. chartX = e.chartY;
  2431. }
  2432. // Drag left handle or top handle
  2433. if (navigator.grabbedLeft) {
  2434. navigator.hasDragged = true;
  2435. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  2436. // Drag right handle or bottom handle
  2437. }
  2438. else if (navigator.grabbedRight) {
  2439. navigator.hasDragged = true;
  2440. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  2441. // Drag scrollbar or open area in navigator
  2442. }
  2443. else if (navigator.grabbedCenter) {
  2444. navigator.hasDragged = true;
  2445. if (chartX < dragOffset) { // outside left
  2446. chartX = dragOffset;
  2447. // outside right
  2448. }
  2449. else if (chartX >
  2450. navigatorSize + dragOffset - range) {
  2451. chartX = navigatorSize + dragOffset - range;
  2452. }
  2453. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  2454. }
  2455. if (navigator.hasDragged &&
  2456. navigator.scrollbar &&
  2457. pick(navigator.scrollbar.options.liveRedraw,
  2458. // By default, don't run live redraw on VML, on touch
  2459. // devices or if the chart is in boost.
  2460. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  2461. e.DOMType = e.type; // DOMType is for IE8
  2462. setTimeout(function () {
  2463. navigator.onMouseUp(e);
  2464. }, 0);
  2465. }
  2466. }
  2467. };
  2468. /**
  2469. * Mouse up event based on x/y mouse position.
  2470. *
  2471. * @private
  2472. * @function Highcharts.Navigator#onMouseUp
  2473. * @param {Highcharts.PointerEventObject} e
  2474. * Mouse event
  2475. * @return {void}
  2476. */
  2477. Navigator.prototype.onMouseUp = function (e) {
  2478. var navigator = this,
  2479. chart = navigator.chart,
  2480. xAxis = navigator.xAxis,
  2481. scrollbar = navigator.scrollbar,
  2482. DOMEvent = e.DOMEvent || e,
  2483. inverted = chart.inverted,
  2484. verb = navigator.rendered && !navigator.hasDragged ?
  2485. 'animate' : 'attr',
  2486. zoomedMax,
  2487. zoomedMin,
  2488. unionExtremes,
  2489. fixedMin,
  2490. fixedMax,
  2491. ext;
  2492. if (
  2493. // MouseUp is called for both, navigator and scrollbar (that order),
  2494. // which causes calling afterSetExtremes twice. Prevent first call
  2495. // by checking if scrollbar is going to set new extremes (#6334)
  2496. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  2497. e.trigger === 'scrollbar') {
  2498. unionExtremes = navigator.getUnionExtremes();
  2499. // When dragging one handle, make sure the other one doesn't change
  2500. if (navigator.zoomedMin === navigator.otherHandlePos) {
  2501. fixedMin = navigator.fixedExtreme;
  2502. }
  2503. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  2504. fixedMax = navigator.fixedExtreme;
  2505. }
  2506. // Snap to right edge (#4076)
  2507. if (navigator.zoomedMax === navigator.size) {
  2508. fixedMax = navigator.reversedExtremes ?
  2509. unionExtremes.dataMin :
  2510. unionExtremes.dataMax;
  2511. }
  2512. // Snap to left edge (#7576)
  2513. if (navigator.zoomedMin === 0) {
  2514. fixedMin = navigator.reversedExtremes ?
  2515. unionExtremes.dataMax :
  2516. unionExtremes.dataMin;
  2517. }
  2518. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  2519. if (defined(ext.min)) {
  2520. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  2521. // Run animation when clicking buttons, scrollbar track etc,
  2522. // but not when dragging handles or scrollbar
  2523. navigator.hasDragged ? false : null, {
  2524. trigger: 'navigator',
  2525. triggerOp: 'navigator-drag',
  2526. DOMEvent: DOMEvent // #1838
  2527. });
  2528. }
  2529. }
  2530. if (e.DOMType !== 'mousemove' &&
  2531. e.DOMType !== 'touchmove') {
  2532. navigator.grabbedLeft = navigator.grabbedRight =
  2533. navigator.grabbedCenter = navigator.fixedWidth =
  2534. navigator.fixedExtreme = navigator.otherHandlePos =
  2535. navigator.hasDragged = navigator.dragOffset = null;
  2536. }
  2537. // Update position of navigator shades, outline and handles (#12573)
  2538. if (navigator.navigatorEnabled &&
  2539. isNumber(navigator.zoomedMin) &&
  2540. isNumber(navigator.zoomedMax)) {
  2541. zoomedMin = Math.round(navigator.zoomedMin);
  2542. zoomedMax = Math.round(navigator.zoomedMax);
  2543. if (navigator.shades) {
  2544. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2545. }
  2546. if (navigator.outline) {
  2547. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2548. }
  2549. if (navigator.navigatorOptions.handles.enabled &&
  2550. Object.keys(navigator.handles).length ===
  2551. navigator.handles.length) {
  2552. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2553. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2554. }
  2555. }
  2556. };
  2557. /**
  2558. * Removes the event handlers attached previously with addEvents.
  2559. *
  2560. * @private
  2561. * @function Highcharts.Navigator#removeEvents
  2562. * @return {void}
  2563. */
  2564. Navigator.prototype.removeEvents = function () {
  2565. if (this.eventsToUnbind) {
  2566. this.eventsToUnbind.forEach(function (unbind) {
  2567. unbind();
  2568. });
  2569. this.eventsToUnbind = void 0;
  2570. }
  2571. this.removeBaseSeriesEvents();
  2572. };
  2573. /**
  2574. * Remove data events.
  2575. *
  2576. * @private
  2577. * @function Highcharts.Navigator#removeBaseSeriesEvents
  2578. * @return {void}
  2579. */
  2580. Navigator.prototype.removeBaseSeriesEvents = function () {
  2581. var baseSeries = this.baseSeries || [];
  2582. if (this.navigatorEnabled && baseSeries[0]) {
  2583. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2584. baseSeries.forEach(function (series) {
  2585. removeEvent(series, 'updatedData', this.updatedDataHandler);
  2586. }, this);
  2587. }
  2588. // We only listen for extremes-events on the first baseSeries
  2589. if (baseSeries[0].xAxis) {
  2590. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2591. }
  2592. }
  2593. };
  2594. /**
  2595. * Initialize the Navigator object
  2596. *
  2597. * @private
  2598. * @function Highcharts.Navigator#init
  2599. *
  2600. * @param {Highcharts.Chart} chart
  2601. */
  2602. Navigator.prototype.init = function (chart) {
  2603. var chartOptions = chart.options,
  2604. navigatorOptions = chartOptions.navigator,
  2605. navigatorEnabled = navigatorOptions.enabled,
  2606. scrollbarOptions = chartOptions.scrollbar,
  2607. scrollbarEnabled = scrollbarOptions.enabled,
  2608. height = navigatorEnabled ? navigatorOptions.height : 0,
  2609. scrollbarHeight = scrollbarEnabled ?
  2610. scrollbarOptions.height :
  2611. 0;
  2612. this.handles = [];
  2613. this.shades = [];
  2614. this.chart = chart;
  2615. this.setBaseSeries();
  2616. this.height = height;
  2617. this.scrollbarHeight = scrollbarHeight;
  2618. this.scrollbarEnabled = scrollbarEnabled;
  2619. this.navigatorEnabled = navigatorEnabled;
  2620. this.navigatorOptions = navigatorOptions;
  2621. this.scrollbarOptions = scrollbarOptions;
  2622. this.outlineHeight = height + scrollbarHeight;
  2623. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  2624. var navigator = this,
  2625. baseSeries = navigator.baseSeries,
  2626. xAxisIndex = chart.xAxis.length,
  2627. yAxisIndex = chart.yAxis.length,
  2628. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  2629. chart.xAxis[0] || { options: {} };
  2630. chart.isDirtyBox = true;
  2631. if (navigator.navigatorEnabled) {
  2632. // an x axis is required for scrollbar also
  2633. navigator.xAxis = new Axis(chart, merge({
  2634. // inherit base xAxis' break and ordinal options
  2635. breaks: baseXaxis.options.breaks,
  2636. ordinal: baseXaxis.options.ordinal
  2637. }, navigatorOptions.xAxis, {
  2638. id: 'navigator-x-axis',
  2639. yAxis: 'navigator-y-axis',
  2640. isX: true,
  2641. type: 'datetime',
  2642. index: xAxisIndex,
  2643. isInternal: true,
  2644. offset: 0,
  2645. keepOrdinalPadding: true,
  2646. startOnTick: false,
  2647. endOnTick: false,
  2648. minPadding: 0,
  2649. maxPadding: 0,
  2650. zoomEnabled: false
  2651. }, chart.inverted ? {
  2652. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  2653. width: height
  2654. } : {
  2655. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  2656. height: height
  2657. }));
  2658. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  2659. id: 'navigator-y-axis',
  2660. alignTicks: false,
  2661. offset: 0,
  2662. index: yAxisIndex,
  2663. isInternal: true,
  2664. reversed: pick((navigatorOptions.yAxis && navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  2665. zoomEnabled: false
  2666. }, chart.inverted ? {
  2667. width: height
  2668. } : {
  2669. height: height
  2670. }));
  2671. // If we have a base series, initialize the navigator series
  2672. if (baseSeries || navigatorOptions.series.data) {
  2673. navigator.updateNavigatorSeries(false);
  2674. // If not, set up an event to listen for added series
  2675. }
  2676. else if (chart.series.length === 0) {
  2677. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  2678. // We've got one, now add it as base
  2679. if (chart.series.length > 0 && !navigator.series) {
  2680. navigator.setBaseSeries();
  2681. navigator.unbindRedraw(); // reset
  2682. }
  2683. });
  2684. }
  2685. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  2686. // Render items, so we can bind events to them:
  2687. navigator.renderElements();
  2688. // Add mouse events
  2689. navigator.addMouseEvents();
  2690. // in case of scrollbar only, fake an x axis to get translation
  2691. }
  2692. else {
  2693. navigator.xAxis = {
  2694. chart: chart,
  2695. navigatorAxis: {
  2696. fake: true
  2697. },
  2698. translate: function (value, reverse) {
  2699. 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;
  2700. return reverse ?
  2701. // from pixel to value
  2702. (value * valueRange / scrollTrackWidth) + min :
  2703. // from value to pixel
  2704. scrollTrackWidth * (value - min) / valueRange;
  2705. },
  2706. toPixels: function (value) {
  2707. return this.translate(value);
  2708. },
  2709. toValue: function (value) {
  2710. return this.translate(value, true);
  2711. }
  2712. };
  2713. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  2714. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  2715. }
  2716. // Initialize the scrollbar
  2717. if (chart.options.scrollbar.enabled) {
  2718. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  2719. margin: navigator.navigatorEnabled ? 0 : 10,
  2720. vertical: chart.inverted
  2721. }), chart);
  2722. addEvent(navigator.scrollbar, 'changed', function (e) {
  2723. var range = navigator.size,
  2724. to = range * this.to,
  2725. from = range * this.from;
  2726. navigator.hasDragged = navigator.scrollbar.hasDragged;
  2727. navigator.render(0, 0, from, to);
  2728. if (this.shouldUpdateExtremes(e.DOMType)) {
  2729. setTimeout(function () {
  2730. navigator.onMouseUp(e);
  2731. });
  2732. }
  2733. });
  2734. }
  2735. // Add data events
  2736. navigator.addBaseSeriesEvents();
  2737. // Add redraw events
  2738. navigator.addChartEvents();
  2739. };
  2740. /**
  2741. * Get the union data extremes of the chart - the outer data extremes of the
  2742. * base X axis and the navigator axis.
  2743. *
  2744. * @private
  2745. * @function Highcharts.Navigator#getUnionExtremes
  2746. * @param {boolean} [returnFalseOnNoBaseSeries]
  2747. * as the param says.
  2748. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  2749. */
  2750. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  2751. var baseAxis = this.chart.xAxis[0],
  2752. navAxis = this.xAxis,
  2753. navAxisOptions = navAxis.options,
  2754. baseAxisOptions = baseAxis.options,
  2755. ret;
  2756. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  2757. ret = {
  2758. dataMin: pick(// #4053
  2759. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  2760. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  2761. };
  2762. }
  2763. return ret;
  2764. };
  2765. /**
  2766. * Set the base series and update the navigator series from this. With a bit
  2767. * of modification we should be able to make this an API method to be called
  2768. * from the outside
  2769. *
  2770. * @private
  2771. * @function Highcharts.Navigator#setBaseSeries
  2772. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  2773. * Additional series options for a navigator
  2774. * @param {boolean} [redraw]
  2775. * Whether to redraw after update.
  2776. * @return {void}
  2777. */
  2778. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  2779. var chart = this.chart,
  2780. baseSeries = this.baseSeries = [];
  2781. baseSeriesOptions = (baseSeriesOptions ||
  2782. chart.options && chart.options.navigator.baseSeries ||
  2783. (chart.series.length ?
  2784. // Find the first non-navigator series (#8430)
  2785. find(chart.series, function (s) {
  2786. return !s.options.isInternal;
  2787. }).index :
  2788. 0));
  2789. // Iterate through series and add the ones that should be shown in
  2790. // navigator.
  2791. (chart.series || []).forEach(function (series, i) {
  2792. if (
  2793. // Don't include existing nav series
  2794. !series.options.isInternal &&
  2795. (series.options.showInNavigator ||
  2796. (i === baseSeriesOptions ||
  2797. series.options.id === baseSeriesOptions) &&
  2798. series.options.showInNavigator !== false)) {
  2799. baseSeries.push(series);
  2800. }
  2801. });
  2802. // When run after render, this.xAxis already exists
  2803. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  2804. this.updateNavigatorSeries(true, redraw);
  2805. }
  2806. };
  2807. /**
  2808. * Update series in the navigator from baseSeries, adding new if does not
  2809. * exist.
  2810. *
  2811. * @private
  2812. * @function Highcharts.Navigator.updateNavigatorSeries
  2813. * @param {boolean} addEvents
  2814. * @param {boolean} [redraw]
  2815. * @return {void}
  2816. */
  2817. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  2818. var navigator = this,
  2819. chart = navigator.chart,
  2820. baseSeries = navigator.baseSeries,
  2821. baseOptions,
  2822. mergedNavSeriesOptions,
  2823. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  2824. baseNavigatorOptions,
  2825. navSeriesMixin = {
  2826. enableMouseTracking: false,
  2827. index: null,
  2828. linkedTo: null,
  2829. group: 'nav',
  2830. padXAxis: false,
  2831. xAxis: 'navigator-x-axis',
  2832. yAxis: 'navigator-y-axis',
  2833. showInLegend: false,
  2834. stacking: void 0,
  2835. isInternal: true,
  2836. states: {
  2837. inactive: {
  2838. opacity: 1
  2839. }
  2840. }
  2841. },
  2842. // Remove navigator series that are no longer in the baseSeries
  2843. navigatorSeries = navigator.series =
  2844. (navigator.series || []).filter(function (navSeries) {
  2845. var base = navSeries.baseSeries;
  2846. if (baseSeries.indexOf(base) < 0) { // Not in array
  2847. // If there is still a base series connected to this
  2848. // series, remove event handler and reference.
  2849. if (base) {
  2850. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  2851. delete base.navigatorSeries;
  2852. }
  2853. // Kill the nav series. It may already have been
  2854. // destroyed (#8715).
  2855. if (navSeries.chart) {
  2856. navSeries.destroy();
  2857. }
  2858. return false;
  2859. }
  2860. return true;
  2861. });
  2862. // Go through each base series and merge the options to create new
  2863. // series
  2864. if (baseSeries && baseSeries.length) {
  2865. baseSeries.forEach(function eachBaseSeries(base) {
  2866. var linkedNavSeries = base.navigatorSeries,
  2867. userNavOptions = extend(
  2868. // Grab color and visibility from base as default
  2869. {
  2870. color: base.color,
  2871. visible: base.visible
  2872. }, !isArray(chartNavigatorSeriesOptions) ?
  2873. chartNavigatorSeriesOptions :
  2874. defaultOptions.navigator.series);
  2875. // Don't update if the series exists in nav and we have disabled
  2876. // adaptToUpdatedData.
  2877. if (linkedNavSeries &&
  2878. navigator.navigatorOptions.adaptToUpdatedData === false) {
  2879. return;
  2880. }
  2881. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  2882. baseOptions = base.options || {};
  2883. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  2884. // The dataLabels options are not merged correctly
  2885. // if the settings are an array, #13847.
  2886. userNavOptions.dataLabels = splat(userNavOptions.dataLabels);
  2887. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  2888. // Once nav series type is resolved, pick correct pointRange
  2889. mergedNavSeriesOptions.pointRange = pick(
  2890. // Stricte set pointRange in options
  2891. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  2892. // Fallback to default values, e.g. `null` for column
  2893. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  2894. // Merge data separately. Do a slice to avoid mutating the
  2895. // navigator options from base series (#4923).
  2896. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  2897. navigator.hasNavigatorData =
  2898. navigator.hasNavigatorData || !!navigatorSeriesData;
  2899. mergedNavSeriesOptions.data =
  2900. navigatorSeriesData ||
  2901. baseOptions.data && baseOptions.data.slice(0);
  2902. // Update or add the series
  2903. if (linkedNavSeries && linkedNavSeries.options) {
  2904. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  2905. }
  2906. else {
  2907. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  2908. base.navigatorSeries.baseSeries = base; // Store ref
  2909. navigatorSeries.push(base.navigatorSeries);
  2910. }
  2911. });
  2912. }
  2913. // If user has defined data (and no base series) or explicitly defined
  2914. // navigator.series as an array, we create these series on top of any
  2915. // base series.
  2916. if (chartNavigatorSeriesOptions.data &&
  2917. !(baseSeries && baseSeries.length) ||
  2918. isArray(chartNavigatorSeriesOptions)) {
  2919. navigator.hasNavigatorData = false;
  2920. // Allow navigator.series to be an array
  2921. chartNavigatorSeriesOptions =
  2922. splat(chartNavigatorSeriesOptions);
  2923. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  2924. navSeriesMixin.name =
  2925. 'Navigator ' + (navigatorSeries.length + 1);
  2926. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  2927. // Since we don't have a base series to pull color from,
  2928. // try to fake it by using color from series with same
  2929. // index. Otherwise pull from the colors array. We need
  2930. // an explicit color as otherwise updates will increment
  2931. // color counter and we'll get a new color for each
  2932. // update of the nav series.
  2933. color: chart.series[i] &&
  2934. !chart.series[i].options.isInternal &&
  2935. chart.series[i].color ||
  2936. chart.options.colors[i] ||
  2937. chart.options.colors[0]
  2938. }, navSeriesMixin, userSeriesOptions);
  2939. mergedNavSeriesOptions.data = userSeriesOptions.data;
  2940. if (mergedNavSeriesOptions.data) {
  2941. navigator.hasNavigatorData = true;
  2942. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  2943. }
  2944. });
  2945. }
  2946. if (addEvents) {
  2947. this.addBaseSeriesEvents();
  2948. }
  2949. };
  2950. /**
  2951. * Add data events.
  2952. * For example when main series is updated we need to recalculate extremes
  2953. *
  2954. * @private
  2955. * @function Highcharts.Navigator#addBaseSeriesEvent
  2956. * @return {void}
  2957. */
  2958. Navigator.prototype.addBaseSeriesEvents = function () {
  2959. var navigator = this,
  2960. baseSeries = navigator.baseSeries || [];
  2961. // Bind modified extremes event to first base's xAxis only.
  2962. // In event of > 1 base-xAxes, the navigator will ignore those.
  2963. // Adding this multiple times to the same axis is no problem, as
  2964. // duplicates should be discarded by the browser.
  2965. if (baseSeries[0] && baseSeries[0].xAxis) {
  2966. baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
  2967. }
  2968. baseSeries.forEach(function (base) {
  2969. // Link base series show/hide to navigator series visibility
  2970. base.eventsToUnbind.push(addEvent(base, 'show', function () {
  2971. if (this.navigatorSeries) {
  2972. this.navigatorSeries.setVisible(true, false);
  2973. }
  2974. }));
  2975. base.eventsToUnbind.push(addEvent(base, 'hide', function () {
  2976. if (this.navigatorSeries) {
  2977. this.navigatorSeries.setVisible(false, false);
  2978. }
  2979. }));
  2980. // Respond to updated data in the base series, unless explicitily
  2981. // not adapting to data changes.
  2982. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2983. if (base.xAxis) {
  2984. base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));
  2985. }
  2986. }
  2987. // Handle series removal
  2988. base.eventsToUnbind.push(addEvent(base, 'remove', function () {
  2989. if (this.navigatorSeries) {
  2990. erase(navigator.series, this.navigatorSeries);
  2991. if (defined(this.navigatorSeries.options)) {
  2992. this.navigatorSeries.remove(false);
  2993. }
  2994. delete this.navigatorSeries;
  2995. }
  2996. }));
  2997. }, this);
  2998. };
  2999. /**
  3000. * Get minimum from all base series connected to the navigator
  3001. * @private
  3002. * @param {number} currentSeriesMin
  3003. * Minium from the current series
  3004. * @return {number} Minimum from all series
  3005. */
  3006. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  3007. return this.baseSeries.reduce(function (min, series) {
  3008. // (#10193)
  3009. return Math.min(min, series.xData ? series.xData[0] : min);
  3010. }, currentSeriesMin);
  3011. };
  3012. /**
  3013. * Set the navigator x axis extremes to reflect the total. The navigator
  3014. * extremes should always be the extremes of the union of all series in the
  3015. * chart as well as the navigator series.
  3016. *
  3017. * @private
  3018. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  3019. */
  3020. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  3021. var xAxis = this.xAxis,
  3022. unionExtremes;
  3023. if (typeof xAxis.getExtremes !== 'undefined') {
  3024. unionExtremes = this.getUnionExtremes(true);
  3025. if (unionExtremes &&
  3026. (unionExtremes.dataMin !== xAxis.min ||
  3027. unionExtremes.dataMax !== xAxis.max)) {
  3028. xAxis.min = unionExtremes.dataMin;
  3029. xAxis.max = unionExtremes.dataMax;
  3030. }
  3031. }
  3032. };
  3033. /**
  3034. * Hook to modify the base axis extremes with information from the Navigator
  3035. *
  3036. * @private
  3037. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  3038. */
  3039. Navigator.prototype.modifyBaseAxisExtremes = function () {
  3040. var baseXAxis = this,
  3041. navigator = baseXAxis.chart.navigator,
  3042. baseExtremes = baseXAxis.getExtremes(),
  3043. baseMin = baseExtremes.min,
  3044. baseMax = baseExtremes.max,
  3045. baseDataMin = baseExtremes.dataMin,
  3046. baseDataMax = baseExtremes.dataMax,
  3047. range = baseMax - baseMin,
  3048. stickToMin = navigator.stickToMin,
  3049. stickToMax = navigator.stickToMax,
  3050. overscroll = pick(baseXAxis.options.overscroll, 0),
  3051. newMax,
  3052. newMin,
  3053. navigatorSeries = navigator.series && navigator.series[0],
  3054. hasSetExtremes = !!baseXAxis.setExtremes,
  3055. // When the extremes have been set by range selector button, don't
  3056. // stick to min or max. The range selector buttons will handle the
  3057. // extremes. (#5489)
  3058. unmutable = baseXAxis.eventArgs &&
  3059. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  3060. if (!unmutable) {
  3061. // If the zoomed range is already at the min, move it to the right
  3062. // as new data comes in
  3063. if (stickToMin) {
  3064. newMin = baseDataMin;
  3065. newMax = newMin + range;
  3066. }
  3067. // If the zoomed range is already at the max, move it to the right
  3068. // as new data comes in
  3069. if (stickToMax) {
  3070. newMax = baseDataMax + overscroll;
  3071. // If stickToMin is true, the new min value is set above
  3072. if (!stickToMin) {
  3073. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  3074. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  3075. navigatorSeries.xData[0] :
  3076. -Number.MAX_VALUE));
  3077. }
  3078. }
  3079. // Update the extremes
  3080. if (hasSetExtremes && (stickToMin || stickToMax)) {
  3081. if (isNumber(newMin)) {
  3082. baseXAxis.min = baseXAxis.userMin = newMin;
  3083. baseXAxis.max = baseXAxis.userMax = newMax;
  3084. }
  3085. }
  3086. }
  3087. // Reset
  3088. navigator.stickToMin =
  3089. navigator.stickToMax = null;
  3090. };
  3091. /**
  3092. * Handler for updated data on the base series. When data is modified, the
  3093. * navigator series must reflect it. This is called from the Chart.redraw
  3094. * function before axis and series extremes are computed.
  3095. *
  3096. * @private
  3097. * @function Highcharts.Navigator#updateDataHandler
  3098. */
  3099. Navigator.prototype.updatedDataHandler = function () {
  3100. var navigator = this.chart.navigator,
  3101. baseSeries = this,
  3102. navigatorSeries = this.navigatorSeries,
  3103. xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  3104. // If the scrollbar is scrolled all the way to the right, keep right as
  3105. // new data comes in.
  3106. navigator.stickToMax = navigator.reversedExtremes ?
  3107. Math.round(navigator.zoomedMin) === 0 :
  3108. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  3109. // Detect whether the zoomed area should stick to the minimum or
  3110. // maximum. If the current axis minimum falls outside the new updated
  3111. // dataset, we must adjust.
  3112. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  3113. (baseSeries.xAxis.min <= xDataMin) &&
  3114. (!this.chart.fixedRange || !navigator.stickToMax);
  3115. // Set the navigator series data to the new data of the base series
  3116. if (navigatorSeries && !navigator.hasNavigatorData) {
  3117. navigatorSeries.options.pointStart = baseSeries.xData[0];
  3118. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  3119. }
  3120. };
  3121. /**
  3122. * Add chart events, like redrawing navigator, when chart requires that.
  3123. *
  3124. * @private
  3125. * @function Highcharts.Navigator#addChartEvents
  3126. * @return {void}
  3127. */
  3128. Navigator.prototype.addChartEvents = function () {
  3129. if (!this.eventsToUnbind) {
  3130. this.eventsToUnbind = [];
  3131. }
  3132. this.eventsToUnbind.push(
  3133. // Move the scrollbar after redraw, like after data updata even if
  3134. // axes don't redraw
  3135. addEvent(this.chart, 'redraw', function () {
  3136. var navigator = this.navigator,
  3137. xAxis = navigator && (navigator.baseSeries &&
  3138. navigator.baseSeries[0] &&
  3139. navigator.baseSeries[0].xAxis ||
  3140. this.xAxis[0]); // #5709, #13114
  3141. if (xAxis) {
  3142. navigator.render(xAxis.min,
  3143. xAxis.max);
  3144. }
  3145. }),
  3146. // Make room for the navigator, can be placed around the chart:
  3147. addEvent(this.chart, 'getMargins', function () {
  3148. var chart = this,
  3149. navigator = chart.navigator,
  3150. marginName = navigator.opposite ?
  3151. 'plotTop' : 'marginBottom';
  3152. if (chart.inverted) {
  3153. marginName = navigator.opposite ?
  3154. 'marginRight' : 'plotLeft';
  3155. }
  3156. chart[marginName] =
  3157. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  3158. navigator.outlineHeight :
  3159. 0) + navigator.navigatorOptions.margin;
  3160. }));
  3161. };
  3162. /**
  3163. * Destroys allocated elements.
  3164. *
  3165. * @private
  3166. * @function Highcharts.Navigator#destroy
  3167. */
  3168. Navigator.prototype.destroy = function () {
  3169. // Disconnect events added in addEvents
  3170. this.removeEvents();
  3171. if (this.xAxis) {
  3172. erase(this.chart.xAxis, this.xAxis);
  3173. erase(this.chart.axes, this.xAxis);
  3174. }
  3175. if (this.yAxis) {
  3176. erase(this.chart.yAxis, this.yAxis);
  3177. erase(this.chart.axes, this.yAxis);
  3178. }
  3179. // Destroy series
  3180. (this.series || []).forEach(function (s) {
  3181. if (s.destroy) {
  3182. s.destroy();
  3183. }
  3184. });
  3185. // Destroy properties
  3186. [
  3187. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  3188. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  3189. 'rendered'
  3190. ].forEach(function (prop) {
  3191. if (this[prop] && this[prop].destroy) {
  3192. this[prop].destroy();
  3193. }
  3194. this[prop] = null;
  3195. }, this);
  3196. // Destroy elements in collection
  3197. [this.handles].forEach(function (coll) {
  3198. destroyObjectProperties(coll);
  3199. }, this);
  3200. };
  3201. return Navigator;
  3202. }());
  3203. // End of prototype
  3204. if (!H.Navigator) {
  3205. H.Navigator = Navigator;
  3206. NavigatorAxis.compose(Axis);
  3207. // For Stock charts. For x only zooming, do not to create the zoom button
  3208. // because X axis zooming is already allowed by the Navigator and Range
  3209. // selector. (#9285)
  3210. addEvent(Chart, 'beforeShowResetZoom', function () {
  3211. var chartOptions = this.options,
  3212. navigator = chartOptions.navigator,
  3213. rangeSelector = chartOptions.rangeSelector;
  3214. if (((navigator && navigator.enabled) ||
  3215. (rangeSelector && rangeSelector.enabled)) &&
  3216. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  3217. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  3218. return false;
  3219. }
  3220. });
  3221. // Initialize navigator for stock charts
  3222. addEvent(Chart, 'beforeRender', function () {
  3223. var options = this.options;
  3224. if (options.navigator.enabled ||
  3225. options.scrollbar.enabled) {
  3226. this.scroller = this.navigator = new Navigator(this);
  3227. }
  3228. });
  3229. // For stock charts, extend the Chart.setChartSize method so that we can set
  3230. // the final top position of the navigator once the height of the chart,
  3231. // including the legend, is determined. #367. We can't use Chart.getMargins,
  3232. // because labels offsets are not calculated yet.
  3233. addEvent(Chart, 'afterSetChartSize', function () {
  3234. var legend = this.legend,
  3235. navigator = this.navigator,
  3236. scrollbarHeight,
  3237. legendOptions,
  3238. xAxis,
  3239. yAxis;
  3240. if (navigator) {
  3241. legendOptions = legend && legend.options;
  3242. xAxis = navigator.xAxis;
  3243. yAxis = navigator.yAxis;
  3244. scrollbarHeight = navigator.scrollbarHeight;
  3245. // Compute the top position
  3246. if (this.inverted) {
  3247. navigator.left = navigator.opposite ?
  3248. this.chartWidth - scrollbarHeight -
  3249. navigator.height :
  3250. this.spacing[3] + scrollbarHeight;
  3251. navigator.top = this.plotTop + scrollbarHeight;
  3252. }
  3253. else {
  3254. navigator.left = this.plotLeft + scrollbarHeight;
  3255. navigator.top = navigator.navigatorOptions.top ||
  3256. this.chartHeight -
  3257. navigator.height -
  3258. scrollbarHeight -
  3259. this.spacing[2] -
  3260. (this.rangeSelector && this.extraBottomMargin ?
  3261. this.rangeSelector.getHeight() :
  3262. 0) -
  3263. ((legendOptions &&
  3264. legendOptions.verticalAlign === 'bottom' &&
  3265. legendOptions.layout !== 'proximate' && // #13392
  3266. legendOptions.enabled &&
  3267. !legendOptions.floating) ?
  3268. legend.legendHeight +
  3269. pick(legendOptions.margin, 10) :
  3270. 0) -
  3271. (this.titleOffset ? this.titleOffset[2] : 0);
  3272. }
  3273. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  3274. if (this.inverted) {
  3275. xAxis.options.left = yAxis.options.left = navigator.left;
  3276. }
  3277. else {
  3278. xAxis.options.top = yAxis.options.top = navigator.top;
  3279. }
  3280. xAxis.setAxisSize();
  3281. yAxis.setAxisSize();
  3282. }
  3283. }
  3284. });
  3285. // Merge options, if no scrolling exists yet
  3286. addEvent(Chart, 'update', function (e) {
  3287. var navigatorOptions = (e.options.navigator || {}),
  3288. scrollbarOptions = (e.options.scrollbar || {});
  3289. if (!this.navigator && !this.scroller &&
  3290. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  3291. merge(true, this.options.navigator, navigatorOptions);
  3292. merge(true, this.options.scrollbar, scrollbarOptions);
  3293. delete e.options.navigator;
  3294. delete e.options.scrollbar;
  3295. }
  3296. });
  3297. // Initialize navigator, if no scrolling exists yet
  3298. addEvent(Chart, 'afterUpdate', function (event) {
  3299. if (!this.navigator && !this.scroller &&
  3300. (this.options.navigator.enabled ||
  3301. this.options.scrollbar.enabled)) {
  3302. this.scroller = this.navigator = new Navigator(this);
  3303. if (pick(event.redraw, true)) {
  3304. this.redraw(event.animation); // #7067
  3305. }
  3306. }
  3307. });
  3308. // Handle adding new series
  3309. addEvent(Chart, 'afterAddSeries', function () {
  3310. if (this.navigator) {
  3311. // Recompute which series should be shown in navigator, and add them
  3312. this.navigator.setBaseSeries(null, false);
  3313. }
  3314. });
  3315. // Handle updating series
  3316. addEvent(Series, 'afterUpdate', function () {
  3317. if (this.chart.navigator && !this.options.isInternal) {
  3318. this.chart.navigator.setBaseSeries(null, false);
  3319. }
  3320. });
  3321. Chart.prototype.callbacks.push(function (chart) {
  3322. var extremes,
  3323. navigator = chart.navigator;
  3324. // Initialize the navigator
  3325. if (navigator && chart.xAxis[0]) {
  3326. extremes = chart.xAxis[0].getExtremes();
  3327. navigator.render(extremes.min, extremes.max);
  3328. }
  3329. });
  3330. }
  3331. H.Navigator = Navigator;
  3332. return H.Navigator;
  3333. });
  3334. _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) {
  3335. /* *
  3336. *
  3337. * (c) 2010-2021 Torstein Honsi
  3338. *
  3339. * License: www.highcharts.com/license
  3340. *
  3341. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3342. *
  3343. * */
  3344. var addEvent = U.addEvent,
  3345. css = U.css,
  3346. defined = U.defined,
  3347. error = U.error,
  3348. pick = U.pick,
  3349. timeUnits = U.timeUnits;
  3350. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  3351. /**
  3352. * Extends the axis with ordinal support.
  3353. * @private
  3354. */
  3355. var OrdinalAxis;
  3356. (function (OrdinalAxis) {
  3357. /* *
  3358. *
  3359. * Classes
  3360. *
  3361. * */
  3362. /**
  3363. * @private
  3364. */
  3365. var Composition = /** @class */ (function () {
  3366. /* *
  3367. *
  3368. * Constructors
  3369. *
  3370. * */
  3371. /**
  3372. * @private
  3373. */
  3374. function Composition(axis) {
  3375. this.index = {};
  3376. this.axis = axis;
  3377. }
  3378. /* *
  3379. *
  3380. * Functions
  3381. *
  3382. * */
  3383. /**
  3384. * Calculate the ordinal positions before tick positions are calculated.
  3385. *
  3386. * @private
  3387. */
  3388. Composition.prototype.beforeSetTickPositions = function () {
  3389. var axis = this.axis,
  3390. ordinal = axis.ordinal,
  3391. len,
  3392. ordinalPositions = [],
  3393. uniqueOrdinalPositions,
  3394. useOrdinal = false,
  3395. dist,
  3396. extremes = axis.getExtremes(),
  3397. min = extremes.min,
  3398. max = extremes.max,
  3399. minIndex,
  3400. maxIndex,
  3401. slope,
  3402. hasBreaks = axis.isXAxis && !!axis.options.breaks,
  3403. isOrdinal = axis.options.ordinal,
  3404. overscrollPointsRange = Number.MAX_VALUE,
  3405. ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries,
  3406. i,
  3407. hasBoostedSeries;
  3408. // Apply the ordinal logic
  3409. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  3410. axis.series.forEach(function (series, i) {
  3411. uniqueOrdinalPositions = [];
  3412. if ((!ignoreHiddenSeries || series.visible !== false) &&
  3413. (series.takeOrdinalPosition !== false || hasBreaks)) {
  3414. // concatenate the processed X data into the existing
  3415. // positions, or the empty array
  3416. ordinalPositions = ordinalPositions.concat(series.processedXData);
  3417. len = ordinalPositions.length;
  3418. // remove duplicates (#1588)
  3419. ordinalPositions.sort(function (a, b) {
  3420. // without a custom function it is sorted as strings
  3421. return a - b;
  3422. });
  3423. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  3424. // Check for a single-point series:
  3425. series.closestPointRange, overscrollPointsRange));
  3426. if (len) {
  3427. i = 0;
  3428. while (i < len - 1) {
  3429. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  3430. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  3431. }
  3432. i++;
  3433. }
  3434. // Check first item:
  3435. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  3436. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  3437. }
  3438. ordinalPositions = uniqueOrdinalPositions;
  3439. }
  3440. }
  3441. if (series.isSeriesBoosting) {
  3442. hasBoostedSeries = true;
  3443. }
  3444. });
  3445. if (hasBoostedSeries) {
  3446. ordinalPositions.length = 0;
  3447. }
  3448. // cache the length
  3449. len = ordinalPositions.length;
  3450. // Check if we really need the overhead of mapping axis data
  3451. // against the ordinal positions. If the series consist of
  3452. // evenly spaced data any way, we don't need any ordinal logic.
  3453. if (len > 2) { // two points have equal distance by default
  3454. dist = ordinalPositions[1] - ordinalPositions[0];
  3455. i = len - 1;
  3456. while (i-- && !useOrdinal) {
  3457. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  3458. useOrdinal = true;
  3459. }
  3460. }
  3461. // When zooming in on a week, prevent axis padding for
  3462. // weekends even though the data within the week is evenly
  3463. // spaced.
  3464. if (!axis.options.keepOrdinalPadding &&
  3465. (ordinalPositions[0] - min > dist ||
  3466. max - ordinalPositions[ordinalPositions.length - 1] >
  3467. dist)) {
  3468. useOrdinal = true;
  3469. }
  3470. }
  3471. else if (axis.options.overscroll) {
  3472. if (len === 2) {
  3473. // Exactly two points, distance for overscroll is fixed:
  3474. overscrollPointsRange =
  3475. ordinalPositions[1] - ordinalPositions[0];
  3476. }
  3477. else if (len === 1) {
  3478. // We have just one point, closest distance is unknown.
  3479. // Assume then it is last point and overscrolled range:
  3480. overscrollPointsRange = axis.options.overscroll;
  3481. ordinalPositions = [
  3482. ordinalPositions[0],
  3483. ordinalPositions[0] + overscrollPointsRange
  3484. ];
  3485. }
  3486. else {
  3487. // In case of zooming in on overscrolled range, stick to
  3488. // the old range:
  3489. overscrollPointsRange = ordinal.overscrollPointsRange;
  3490. }
  3491. }
  3492. // Record the slope and offset to compute the linear values from
  3493. // the array index. Since the ordinal positions may exceed the
  3494. // current range, get the start and end positions within it
  3495. // (#719, #665b)
  3496. if (useOrdinal || axis.forceOrdinal) {
  3497. if (axis.options.overscroll) {
  3498. ordinal.overscrollPointsRange = overscrollPointsRange;
  3499. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  3500. }
  3501. // Register
  3502. ordinal.positions = ordinalPositions;
  3503. // This relies on the ordinalPositions being set. Use
  3504. // Math.max and Math.min to prevent padding on either sides
  3505. // of the data.
  3506. minIndex = axis.ordinal2lin(// #5979
  3507. Math.max(min, ordinalPositions[0]), true);
  3508. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  3509. // Set the slope and offset of the values compared to the
  3510. // indices in the ordinal positions
  3511. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  3512. ordinal.offset = min - (minIndex * slope);
  3513. }
  3514. else {
  3515. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  3516. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  3517. void 0;
  3518. }
  3519. }
  3520. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  3521. ordinal.groupIntervalFactor = null; // reset for next run
  3522. };
  3523. /**
  3524. * Get the ordinal positions for the entire data set. This is necessary
  3525. * in chart panning because we need to find out what points or data
  3526. * groups are available outside the visible range. When a panning
  3527. * operation starts, if an index for the given grouping does not exists,
  3528. * it is created and cached. This index is deleted on updated data, so
  3529. * it will be regenerated the next time a panning operation starts.
  3530. *
  3531. * @private
  3532. */
  3533. Composition.prototype.getExtendedPositions = function () {
  3534. var ordinal = this,
  3535. axis = ordinal.axis,
  3536. axisProto = axis.constructor.prototype,
  3537. chart = axis.chart,
  3538. grouping = axis.series[0].currentDataGrouping,
  3539. ordinalIndex = ordinal.index,
  3540. key = grouping ?
  3541. grouping.count + grouping.unitName :
  3542. 'raw',
  3543. overscroll = axis.options.overscroll,
  3544. extremes = axis.getExtremes(),
  3545. fakeAxis,
  3546. fakeSeries;
  3547. // If this is the first time, or the ordinal index is deleted by
  3548. // updatedData,
  3549. // create it.
  3550. if (!ordinalIndex) {
  3551. ordinalIndex = ordinal.index = {};
  3552. }
  3553. if (!ordinalIndex[key]) {
  3554. // Create a fake axis object where the extended ordinal
  3555. // positions are emulated
  3556. fakeAxis = {
  3557. series: [],
  3558. chart: chart,
  3559. forceOrdinal: false,
  3560. getExtremes: function () {
  3561. return {
  3562. min: extremes.dataMin,
  3563. max: extremes.dataMax + overscroll
  3564. };
  3565. },
  3566. getGroupPixelWidth: axisProto.getGroupPixelWidth,
  3567. getTimeTicks: axisProto.getTimeTicks,
  3568. options: {
  3569. ordinal: true
  3570. },
  3571. ordinal: {
  3572. getGroupIntervalFactor: this.getGroupIntervalFactor
  3573. },
  3574. ordinal2lin: axisProto.ordinal2lin,
  3575. val2lin: axisProto.val2lin // #2590
  3576. };
  3577. fakeAxis.ordinal.axis = fakeAxis;
  3578. // Add the fake series to hold the full data, then apply
  3579. // processData to it
  3580. axis.series.forEach(function (series) {
  3581. fakeSeries = {
  3582. xAxis: fakeAxis,
  3583. xData: series.xData.slice(),
  3584. chart: chart,
  3585. destroyGroupedData: H.noop,
  3586. getProcessedData: Series.prototype.getProcessedData
  3587. };
  3588. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  3589. fakeSeries.options = {
  3590. dataGrouping: grouping ? {
  3591. enabled: true,
  3592. forced: true,
  3593. // doesn't matter which, use the fastest
  3594. approximation: 'open',
  3595. units: [[
  3596. grouping.unitName,
  3597. [grouping.count]
  3598. ]]
  3599. } : {
  3600. enabled: false
  3601. }
  3602. };
  3603. fakeAxis.series.push(fakeSeries);
  3604. series.processData.apply(fakeSeries);
  3605. // Force to use the ordinal when points are evenly spaced
  3606. // (e.g. weeks), #3825.
  3607. if (fakeSeries.closestPointRange !== fakeSeries.basePointRange && fakeSeries.currentDataGrouping) {
  3608. fakeAxis.forceOrdinal = true;
  3609. }
  3610. });
  3611. // Run beforeSetTickPositions to compute the ordinalPositions
  3612. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  3613. // Cache it
  3614. ordinalIndex[key] = fakeAxis.ordinal.positions;
  3615. }
  3616. return ordinalIndex[key];
  3617. };
  3618. /**
  3619. * Find the factor to estimate how wide the plot area would have been if
  3620. * ordinal gaps were included. This value is used to compute an imagined
  3621. * plot width in order to establish the data grouping interval.
  3622. *
  3623. * A real world case is the intraday-candlestick example. Without this
  3624. * logic, it would show the correct data grouping when viewing a range
  3625. * within each day, but once moving the range to include the gap between
  3626. * two days, the interval would include the cut-away night hours and the
  3627. * data grouping would be wrong. So the below method tries to compensate
  3628. * by identifying the most common point interval, in this case days.
  3629. *
  3630. * An opposite case is presented in issue #718. We have a long array of
  3631. * daily data, then one point is appended one hour after the last point.
  3632. * We expect the data grouping not to change.
  3633. *
  3634. * In the future, if we find cases where this estimation doesn't work
  3635. * optimally, we might need to add a second pass to the data grouping
  3636. * logic, where we do another run with a greater interval if the number
  3637. * of data groups is more than a certain fraction of the desired group
  3638. * count.
  3639. *
  3640. * @private
  3641. */
  3642. Composition.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
  3643. var ordinal = this,
  3644. axis = ordinal.axis,
  3645. i,
  3646. processedXData = series.processedXData,
  3647. len = processedXData.length,
  3648. distances = [],
  3649. median,
  3650. groupIntervalFactor = ordinal.groupIntervalFactor;
  3651. // Only do this computation for the first series, let the other
  3652. // inherit it (#2416)
  3653. if (!groupIntervalFactor) {
  3654. // Register all the distances in an array
  3655. for (i = 0; i < len - 1; i++) {
  3656. distances[i] =
  3657. processedXData[i + 1] - processedXData[i];
  3658. }
  3659. // Sort them and find the median
  3660. distances.sort(function (a, b) {
  3661. return a - b;
  3662. });
  3663. median = distances[Math.floor(len / 2)];
  3664. // Compensate for series that don't extend through the entire
  3665. // axis extent. #1675.
  3666. xMin = Math.max(xMin, processedXData[0]);
  3667. xMax = Math.min(xMax, processedXData[len - 1]);
  3668. ordinal.groupIntervalFactor = groupIntervalFactor =
  3669. (len * median) / (xMax - xMin);
  3670. }
  3671. // Return the factor needed for data grouping
  3672. return groupIntervalFactor;
  3673. };
  3674. /**
  3675. * Get ticks for an ordinal axis within a range where points don't
  3676. * exist. It is required when overscroll is enabled. We can't base on
  3677. * points, because we may not have any, so we use approximated
  3678. * pointRange and generate these ticks between Axis.dataMax,
  3679. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  3680. * navigator scrolling.
  3681. *
  3682. * @private
  3683. */
  3684. Composition.prototype.getOverscrollPositions = function () {
  3685. var ordinal = this,
  3686. axis = ordinal.axis,
  3687. extraRange = axis.options.overscroll,
  3688. distance = ordinal.overscrollPointsRange,
  3689. positions = [],
  3690. max = axis.dataMax;
  3691. if (defined(distance)) {
  3692. // Max + pointRange because we need to scroll to the last
  3693. positions.push(max);
  3694. while (max <= axis.dataMax + extraRange) {
  3695. max += distance;
  3696. positions.push(max);
  3697. }
  3698. }
  3699. return positions;
  3700. };
  3701. /**
  3702. * Make the tick intervals closer because the ordinal gaps make the
  3703. * ticks spread out or cluster.
  3704. *
  3705. * @private
  3706. */
  3707. Composition.prototype.postProcessTickInterval = function (tickInterval) {
  3708. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  3709. // This is a case where this algorithm doesn't work optimally. In
  3710. // this case, the tick labels are spread out per week, but all the
  3711. // gaps reside within weeks. So we have a situation where the labels
  3712. // are courser than the ordinal gaps, and thus the tick interval
  3713. // should not be altered.
  3714. var ordinal = this,
  3715. axis = ordinal.axis,
  3716. ordinalSlope = ordinal.slope,
  3717. ret;
  3718. if (ordinalSlope) {
  3719. if (!axis.options.breaks) {
  3720. ret = tickInterval / (ordinalSlope / axis.closestPointRange);
  3721. }
  3722. else {
  3723. ret = axis.closestPointRange || tickInterval; // #7275
  3724. }
  3725. }
  3726. else {
  3727. ret = tickInterval;
  3728. }
  3729. return ret;
  3730. };
  3731. return Composition;
  3732. }());
  3733. OrdinalAxis.Composition = Composition;
  3734. /* *
  3735. *
  3736. * Functions
  3737. *
  3738. * */
  3739. /**
  3740. * Extends the axis with ordinal support.
  3741. *
  3742. * @private
  3743. *
  3744. * @param AxisClass
  3745. * Axis class to extend.
  3746. *
  3747. * @param ChartClass
  3748. * Chart class to use.
  3749. *
  3750. * @param SeriesClass
  3751. * Series class to use.
  3752. */
  3753. function compose(AxisClass, ChartClass, SeriesClass) {
  3754. AxisClass.keepProps.push('ordinal');
  3755. var axisProto = AxisClass.prototype;
  3756. /**
  3757. * In an ordinal axis, there might be areas with dense consentrations of
  3758. * points, then large gaps between some. Creating equally distributed
  3759. * ticks over this entire range may lead to a huge number of ticks that
  3760. * will later be removed. So instead, break the positions up in
  3761. * segments, find the tick positions for each segment then concatenize
  3762. * them. This method is used from both data grouping logic and X axis
  3763. * tick position logic.
  3764. *
  3765. * @private
  3766. */
  3767. AxisClass.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  3768. if (positions === void 0) { positions = []; }
  3769. if (closestDistance === void 0) { closestDistance = 0; }
  3770. var start = 0,
  3771. end,
  3772. segmentPositions,
  3773. higherRanks = {},
  3774. hasCrossedHigherRank,
  3775. info,
  3776. posLength,
  3777. outsideMax,
  3778. groupPositions = [],
  3779. lastGroupPosition = -Number.MAX_VALUE,
  3780. tickPixelIntervalOption = this.options.tickPixelInterval,
  3781. time = this.chart.time,
  3782. // Record all the start positions of a segment, to use when
  3783. // deciding what's a gap in the data.
  3784. segmentStarts = [];
  3785. // The positions are not always defined, for example for ordinal
  3786. // positions when data has regular interval (#1557, #2090)
  3787. if ((!this.options.ordinal && !this.options.breaks) ||
  3788. !positions ||
  3789. positions.length < 3 ||
  3790. typeof min === 'undefined') {
  3791. return time.getTimeTicks.apply(time, arguments);
  3792. }
  3793. // Analyze the positions array to split it into segments on gaps
  3794. // larger than 5 times the closest distance. The closest distance is
  3795. // already found at this point, so we reuse that instead of
  3796. // computing it again.
  3797. posLength = positions.length;
  3798. for (end = 0; end < posLength; end++) {
  3799. outsideMax = end && positions[end - 1] > max;
  3800. if (positions[end] < min) { // Set the last position before min
  3801. start = end;
  3802. }
  3803. if (end === posLength - 1 ||
  3804. positions[end + 1] - positions[end] > closestDistance * 5 ||
  3805. outsideMax) {
  3806. // For each segment, calculate the tick positions from the
  3807. // getTimeTicks utility function. The interval will be the
  3808. // same regardless of how long the segment is.
  3809. if (positions[end] > lastGroupPosition) { // #1475
  3810. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  3811. // Prevent duplicate groups, for example for multiple
  3812. // segments within one larger time frame (#1475)
  3813. while (segmentPositions.length &&
  3814. segmentPositions[0] <= lastGroupPosition) {
  3815. segmentPositions.shift();
  3816. }
  3817. if (segmentPositions.length) {
  3818. lastGroupPosition =
  3819. segmentPositions[segmentPositions.length - 1];
  3820. }
  3821. segmentStarts.push(groupPositions.length);
  3822. groupPositions = groupPositions.concat(segmentPositions);
  3823. }
  3824. // Set start of next segment
  3825. start = end + 1;
  3826. }
  3827. if (outsideMax) {
  3828. break;
  3829. }
  3830. }
  3831. // Get the grouping info from the last of the segments. The info is
  3832. // the same for all segments.
  3833. if (segmentPositions) {
  3834. info = segmentPositions.info;
  3835. // Optionally identify ticks with higher rank, for example
  3836. // when the ticks have crossed midnight.
  3837. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  3838. end = groupPositions.length - 1;
  3839. // Compare points two by two
  3840. for (start = 1; start < end; start++) {
  3841. if (time.dateFormat('%d', groupPositions[start]) !==
  3842. time.dateFormat('%d', groupPositions[start - 1])) {
  3843. higherRanks[groupPositions[start]] = 'day';
  3844. hasCrossedHigherRank = true;
  3845. }
  3846. }
  3847. // If the complete array has crossed midnight, we want
  3848. // to mark the first positions also as higher rank
  3849. if (hasCrossedHigherRank) {
  3850. higherRanks[groupPositions[0]] = 'day';
  3851. }
  3852. info.higherRanks = higherRanks;
  3853. }
  3854. // Save the info
  3855. info.segmentStarts = segmentStarts;
  3856. groupPositions.info = info;
  3857. }
  3858. else {
  3859. error(12, false, this.chart);
  3860. }
  3861. // Don't show ticks within a gap in the ordinal axis, where the
  3862. // space between two points is greater than a portion of the tick
  3863. // pixel interval
  3864. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  3865. var length_1 = groupPositions.length,
  3866. i = length_1,
  3867. itemToRemove = void 0,
  3868. translated = void 0,
  3869. translatedArr = [],
  3870. lastTranslated = void 0,
  3871. medianDistance = void 0,
  3872. distance = void 0,
  3873. distances = [];
  3874. // Find median pixel distance in order to keep a reasonably even
  3875. // distance between ticks (#748)
  3876. while (i--) {
  3877. translated = this.translate(groupPositions[i]);
  3878. if (lastTranslated) {
  3879. distances[i] = lastTranslated - translated;
  3880. }
  3881. translatedArr[i] = lastTranslated = translated;
  3882. }
  3883. distances.sort();
  3884. medianDistance = distances[Math.floor(distances.length / 2)];
  3885. if (medianDistance < tickPixelIntervalOption * 0.6) {
  3886. medianDistance = null;
  3887. }
  3888. // Now loop over again and remove ticks where needed
  3889. i = groupPositions[length_1 - 1] > max ? length_1 - 1 : length_1; // #817
  3890. lastTranslated = void 0;
  3891. while (i--) {
  3892. translated = translatedArr[i];
  3893. distance = Math.abs(lastTranslated - translated);
  3894. // #4175 - when axis is reversed, the distance, is negative
  3895. // but tickPixelIntervalOption positive, so we need to
  3896. // compare the same values
  3897. // Remove ticks that are closer than 0.6 times the pixel
  3898. // interval from the one to the right, but not if it is
  3899. // close to the median distance (#748).
  3900. if (lastTranslated &&
  3901. distance < tickPixelIntervalOption * 0.8 &&
  3902. (medianDistance === null || distance < medianDistance * 0.8)) {
  3903. // Is this a higher ranked position with a normal
  3904. // position to the right?
  3905. if (higherRanks[groupPositions[i]] &&
  3906. !higherRanks[groupPositions[i + 1]]) {
  3907. // Yes: remove the lower ranked neighbour to the
  3908. // right
  3909. itemToRemove = i + 1;
  3910. lastTranslated = translated; // #709
  3911. }
  3912. else {
  3913. // No: remove this one
  3914. itemToRemove = i;
  3915. }
  3916. groupPositions.splice(itemToRemove, 1);
  3917. }
  3918. else {
  3919. lastTranslated = translated;
  3920. }
  3921. }
  3922. }
  3923. return groupPositions;
  3924. };
  3925. /**
  3926. * Translate from linear (internal) to axis value.
  3927. *
  3928. * @private
  3929. * @function Highcharts.Axis#lin2val
  3930. *
  3931. * @param {number} val
  3932. * The linear abstracted value.
  3933. *
  3934. * @param {boolean} [fromIndex]
  3935. * Translate from an index in the ordinal positions rather than a
  3936. * value.
  3937. *
  3938. * @return {number}
  3939. */
  3940. axisProto.lin2val = function (val, fromIndex) {
  3941. var axis = this,
  3942. ordinal = axis.ordinal,
  3943. ordinalPositions = ordinal.positions,
  3944. ret;
  3945. // the visible range contains only equally spaced values
  3946. if (!ordinalPositions) {
  3947. ret = val;
  3948. }
  3949. else {
  3950. var ordinalSlope = ordinal.slope,
  3951. ordinalOffset = ordinal.offset,
  3952. i = ordinalPositions.length - 1,
  3953. linearEquivalentLeft = void 0,
  3954. linearEquivalentRight = void 0,
  3955. distance = void 0;
  3956. // Handle the case where we translate from the index directly,
  3957. // used only when panning an ordinal axis
  3958. if (fromIndex) {
  3959. if (val < 0) { // out of range, in effect panning to the left
  3960. val = ordinalPositions[0];
  3961. }
  3962. else if (val > i) { // out of range, panning to the right
  3963. val = ordinalPositions[i];
  3964. }
  3965. else { // split it up
  3966. i = Math.floor(val);
  3967. distance = val - i; // the decimal
  3968. }
  3969. // Loop down along the ordinal positions. When the linear
  3970. // equivalent of i matches an ordinal position, interpolate
  3971. // between the left and right values.
  3972. }
  3973. else {
  3974. while (i--) {
  3975. linearEquivalentLeft =
  3976. (ordinalSlope * i) + ordinalOffset;
  3977. if (val >= linearEquivalentLeft) {
  3978. linearEquivalentRight =
  3979. (ordinalSlope *
  3980. (i + 1)) +
  3981. ordinalOffset;
  3982. // something between 0 and 1
  3983. distance = (val - linearEquivalentLeft) /
  3984. (linearEquivalentRight - linearEquivalentLeft);
  3985. break;
  3986. }
  3987. }
  3988. }
  3989. // If the index is within the range of the ordinal positions,
  3990. // return the associated or interpolated value. If not, just
  3991. // return the value.
  3992. return (typeof distance !== 'undefined' &&
  3993. typeof ordinalPositions[i] !== 'undefined' ?
  3994. ordinalPositions[i] + (distance ?
  3995. distance *
  3996. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  3997. 0) :
  3998. val);
  3999. }
  4000. return ret;
  4001. };
  4002. /**
  4003. * Translate from a linear axis value to the corresponding ordinal axis
  4004. * position. If there are no gaps in the ordinal axis this will be the
  4005. * same. The translated value is the value that the point would have if
  4006. * the axis were linear, using the same min and max.
  4007. *
  4008. * @private
  4009. * @function Highcharts.Axis#val2lin
  4010. *
  4011. * @param {number} val
  4012. * The axis value.
  4013. *
  4014. * @param {boolean} [toIndex]
  4015. * Whether to return the index in the ordinalPositions or the new value.
  4016. *
  4017. * @return {number}
  4018. */
  4019. axisProto.val2lin = function (val, toIndex) {
  4020. var axis = this,
  4021. ordinal = axis.ordinal,
  4022. ordinalPositions = ordinal.positions,
  4023. ret;
  4024. if (!ordinalPositions) {
  4025. ret = val;
  4026. }
  4027. else {
  4028. var ordinalLength = ordinalPositions.length,
  4029. i = void 0,
  4030. distance = void 0,
  4031. ordinalIndex = void 0;
  4032. // first look for an exact match in the ordinalpositions array
  4033. i = ordinalLength;
  4034. while (i--) {
  4035. if (ordinalPositions[i] === val) {
  4036. ordinalIndex = i;
  4037. break;
  4038. }
  4039. }
  4040. // if that failed, find the intermediate position between the
  4041. // two nearest values
  4042. i = ordinalLength - 1;
  4043. while (i--) {
  4044. if (val > ordinalPositions[i] || i === 0) { // interpolate
  4045. // something between 0 and 1
  4046. distance = (val - ordinalPositions[i]) /
  4047. (ordinalPositions[i + 1] - ordinalPositions[i]);
  4048. ordinalIndex = i + distance;
  4049. break;
  4050. }
  4051. }
  4052. ret = toIndex ?
  4053. ordinalIndex :
  4054. ordinal.slope *
  4055. (ordinalIndex || 0) +
  4056. ordinal.offset;
  4057. }
  4058. return ret;
  4059. };
  4060. // Record this to prevent overwriting by broken-axis module (#5979)
  4061. axisProto.ordinal2lin = axisProto.val2lin;
  4062. /* eslint-disable no-invalid-this */
  4063. addEvent(AxisClass, 'afterInit', function () {
  4064. var axis = this;
  4065. if (!axis.ordinal) {
  4066. axis.ordinal = new OrdinalAxis.Composition(axis);
  4067. }
  4068. });
  4069. addEvent(AxisClass, 'foundExtremes', function () {
  4070. var axis = this;
  4071. if (axis.isXAxis &&
  4072. defined(axis.options.overscroll) &&
  4073. axis.max === axis.dataMax &&
  4074. (
  4075. // Panning is an execption. We don't want to apply
  4076. // overscroll when panning over the dataMax
  4077. !axis.chart.mouseIsDown ||
  4078. axis.isInternal) && (
  4079. // Scrollbar buttons are the other execption:
  4080. !axis.eventArgs ||
  4081. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  4082. axis.max += axis.options.overscroll;
  4083. // Live data and buttons require translation for the min:
  4084. if (!axis.isInternal && defined(axis.userMin)) {
  4085. axis.min += axis.options.overscroll;
  4086. }
  4087. }
  4088. });
  4089. // For ordinal axis, that loads data async, redraw axis after data is
  4090. // loaded. If we don't do that, axis will have the same extremes as
  4091. // previously, but ordinal positions won't be calculated. See #10290
  4092. addEvent(AxisClass, 'afterSetScale', function () {
  4093. var axis = this;
  4094. if (axis.horiz && !axis.isDirty) {
  4095. axis.isDirty = axis.isOrdinal &&
  4096. axis.chart.navigator &&
  4097. !axis.chart.navigator.adaptToUpdatedData;
  4098. }
  4099. });
  4100. addEvent(AxisClass, 'initialAxisTranslation', function () {
  4101. var axis = this;
  4102. if (axis.ordinal) {
  4103. axis.ordinal.beforeSetTickPositions();
  4104. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  4105. }
  4106. });
  4107. // Extending the Chart.pan method for ordinal axes
  4108. addEvent(ChartClass, 'pan', function (e) {
  4109. var chart = this,
  4110. xAxis = chart.xAxis[0],
  4111. overscroll = xAxis.options.overscroll,
  4112. chartX = e.originalEvent.chartX,
  4113. panning = chart.options.chart.panning,
  4114. runBase = false;
  4115. if (panning &&
  4116. panning.type !== 'y' &&
  4117. xAxis.options.ordinal &&
  4118. xAxis.series.length) {
  4119. var mouseDownX = chart.mouseDownX,
  4120. extremes = xAxis.getExtremes(),
  4121. dataMax = extremes.dataMax,
  4122. min = extremes.min,
  4123. max = extremes.max,
  4124. trimmedRange = void 0,
  4125. hoverPoints = chart.hoverPoints,
  4126. closestPointRange = (xAxis.closestPointRange ||
  4127. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)),
  4128. pointPixelWidth = (xAxis.translationSlope *
  4129. (xAxis.ordinal.slope || closestPointRange)),
  4130. // how many ordinal units did we move?
  4131. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  4132. // get index of all the chart's points
  4133. extendedAxis = { ordinal: { positions: xAxis.ordinal.getExtendedPositions() } },
  4134. ordinalPositions = void 0,
  4135. searchAxisLeft = void 0,
  4136. lin2val = xAxis.lin2val,
  4137. val2lin = xAxis.val2lin,
  4138. searchAxisRight = void 0;
  4139. // we have an ordinal axis, but the data is equally spaced
  4140. if (!extendedAxis.ordinal.positions) {
  4141. runBase = true;
  4142. }
  4143. else if (Math.abs(movedUnits) > 1) {
  4144. // Remove active points for shared tooltip
  4145. if (hoverPoints) {
  4146. hoverPoints.forEach(function (point) {
  4147. point.setState();
  4148. });
  4149. }
  4150. if (movedUnits < 0) {
  4151. searchAxisLeft = extendedAxis;
  4152. searchAxisRight = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4153. }
  4154. else {
  4155. searchAxisLeft = xAxis.ordinal.positions ? xAxis : extendedAxis;
  4156. searchAxisRight = extendedAxis;
  4157. }
  4158. // In grouped data series, the last ordinal position
  4159. // represents the grouped data, which is to the left of the
  4160. // real data max. If we don't compensate for this, we will
  4161. // be allowed to pan grouped data series passed the right of
  4162. // the plot area.
  4163. ordinalPositions = searchAxisRight.ordinal.positions;
  4164. if (dataMax >
  4165. ordinalPositions[ordinalPositions.length - 1]) {
  4166. ordinalPositions.push(dataMax);
  4167. }
  4168. // Get the new min and max values by getting the ordinal
  4169. // index for the current extreme, then add the moved units
  4170. // and translate back to values. This happens on the
  4171. // extended ordinal positions if the new position is out of
  4172. // range, else it happens on the current x axis which is
  4173. // smaller and faster.
  4174. chart.fixedRange = max - min;
  4175. trimmedRange = xAxis.navigatorAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  4176. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  4177. true // translate from index
  4178. ]), lin2val.apply(searchAxisRight, [
  4179. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  4180. true // translate from index
  4181. ]));
  4182. // Apply it if it is within the available data range
  4183. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  4184. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  4185. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  4186. }
  4187. chart.mouseDownX = chartX; // set new reference for next run
  4188. css(chart.container, { cursor: 'move' });
  4189. }
  4190. }
  4191. else {
  4192. runBase = true;
  4193. }
  4194. // revert to the linear chart.pan version
  4195. if (runBase || (panning && /y/.test(panning.type))) {
  4196. if (overscroll) {
  4197. xAxis.max = xAxis.dataMax + overscroll;
  4198. }
  4199. }
  4200. else {
  4201. e.preventDefault();
  4202. }
  4203. });
  4204. addEvent(SeriesClass, 'updatedData', function () {
  4205. var xAxis = this.xAxis;
  4206. // Destroy the extended ordinal index on updated data
  4207. if (xAxis && xAxis.options.ordinal) {
  4208. delete xAxis.ordinal.index;
  4209. }
  4210. });
  4211. /* eslint-enable no-invalid-this */
  4212. }
  4213. OrdinalAxis.compose = compose;
  4214. })(OrdinalAxis || (OrdinalAxis = {}));
  4215. OrdinalAxis.compose(Axis, Chart, Series); // @todo move to StockChart, remove from master
  4216. return OrdinalAxis;
  4217. });
  4218. _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) {
  4219. /* *
  4220. *
  4221. * (c) 2009-2021 Torstein Honsi
  4222. *
  4223. * License: www.highcharts.com/license
  4224. *
  4225. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4226. *
  4227. * */
  4228. var addEvent = U.addEvent,
  4229. find = U.find,
  4230. fireEvent = U.fireEvent,
  4231. isArray = U.isArray,
  4232. isNumber = U.isNumber,
  4233. pick = U.pick;
  4234. /* eslint-disable valid-jsdoc */
  4235. /**
  4236. * Provides support for broken axes.
  4237. * @private
  4238. * @class
  4239. */
  4240. var BrokenAxisAdditions = /** @class */ (function () {
  4241. /* *
  4242. *
  4243. * Constructors
  4244. *
  4245. * */
  4246. function BrokenAxisAdditions(axis) {
  4247. this.hasBreaks = false;
  4248. this.axis = axis;
  4249. }
  4250. /* *
  4251. *
  4252. * Static Functions
  4253. *
  4254. * */
  4255. /**
  4256. * @private
  4257. */
  4258. BrokenAxisAdditions.isInBreak = function (brk, val) {
  4259. var ret,
  4260. repeat = brk.repeat || Infinity,
  4261. from = brk.from,
  4262. length = brk.to - brk.from,
  4263. test = (val >= from ?
  4264. (val - from) % repeat :
  4265. repeat - ((from - val) % repeat));
  4266. if (!brk.inclusive) {
  4267. ret = test < length && test !== 0;
  4268. }
  4269. else {
  4270. ret = test <= length;
  4271. }
  4272. return ret;
  4273. };
  4274. /**
  4275. * @private
  4276. */
  4277. BrokenAxisAdditions.lin2Val = function (val) {
  4278. var axis = this;
  4279. var brokenAxis = axis.brokenAxis;
  4280. var breakArray = brokenAxis && brokenAxis.breakArray;
  4281. if (!breakArray || !isNumber(val)) {
  4282. return val;
  4283. }
  4284. var nval = val,
  4285. brk,
  4286. i;
  4287. for (i = 0; i < breakArray.length; i++) {
  4288. brk = breakArray[i];
  4289. if (brk.from >= nval) {
  4290. break;
  4291. }
  4292. else if (brk.to < nval) {
  4293. nval += brk.len;
  4294. }
  4295. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  4296. nval += brk.len;
  4297. }
  4298. }
  4299. return nval;
  4300. };
  4301. /**
  4302. * @private
  4303. */
  4304. BrokenAxisAdditions.val2Lin = function (val) {
  4305. var axis = this;
  4306. var brokenAxis = axis.brokenAxis;
  4307. var breakArray = brokenAxis && brokenAxis.breakArray;
  4308. if (!breakArray || !isNumber(val)) {
  4309. return val;
  4310. }
  4311. var nval = val,
  4312. brk,
  4313. i;
  4314. for (i = 0; i < breakArray.length; i++) {
  4315. brk = breakArray[i];
  4316. if (brk.to <= val) {
  4317. nval -= brk.len;
  4318. }
  4319. else if (brk.from >= val) {
  4320. break;
  4321. }
  4322. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  4323. nval -= (val - brk.from);
  4324. break;
  4325. }
  4326. }
  4327. return nval;
  4328. };
  4329. /* *
  4330. *
  4331. * Functions
  4332. *
  4333. * */
  4334. /**
  4335. * Returns the first break found where the x is larger then break.from and
  4336. * smaller then break.to.
  4337. *
  4338. * @param {number} x
  4339. * The number which should be within a break.
  4340. *
  4341. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  4342. * The array of breaks to search within.
  4343. *
  4344. * @return {Highcharts.XAxisBreaksOptions|undefined}
  4345. * Returns the first break found that matches, returns false if no break is
  4346. * found.
  4347. */
  4348. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  4349. return find(breaks, function (b) {
  4350. return b.from < x && x < b.to;
  4351. });
  4352. };
  4353. /**
  4354. * @private
  4355. */
  4356. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  4357. var brokenAxis = this;
  4358. var axis = brokenAxis.axis;
  4359. var breaks = axis.options.breaks || [],
  4360. i = breaks.length,
  4361. inbrk,
  4362. keep,
  4363. ret;
  4364. if (i && isNumber(val)) {
  4365. while (i--) {
  4366. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  4367. inbrk = true;
  4368. if (!keep) {
  4369. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  4370. }
  4371. }
  4372. }
  4373. if (inbrk && testKeep) {
  4374. ret = inbrk && !keep;
  4375. }
  4376. else {
  4377. ret = inbrk;
  4378. }
  4379. }
  4380. return ret;
  4381. };
  4382. /**
  4383. * Dynamically set or unset breaks in an axis. This function in lighter than
  4384. * usin Axis.update, and it also preserves animation.
  4385. *
  4386. * @private
  4387. * @function Highcharts.Axis#setBreaks
  4388. *
  4389. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  4390. * The breaks to add. When `undefined` it removes existing breaks.
  4391. *
  4392. * @param {boolean} [redraw=true]
  4393. * Whether to redraw the chart immediately.
  4394. *
  4395. * @return {void}
  4396. */
  4397. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  4398. var brokenAxis = this;
  4399. var axis = brokenAxis.axis;
  4400. var hasBreaks = (isArray(breaks) && !!breaks.length);
  4401. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  4402. brokenAxis.hasBreaks = hasBreaks;
  4403. axis.options.breaks = axis.userOptions.breaks = breaks;
  4404. axis.forceRedraw = true; // Force recalculation in setScale
  4405. // Recalculate series related to the axis.
  4406. axis.series.forEach(function (series) {
  4407. series.isDirty = true;
  4408. });
  4409. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  4410. // Revert to prototype functions
  4411. delete axis.val2lin;
  4412. delete axis.lin2val;
  4413. }
  4414. if (hasBreaks) {
  4415. axis.userOptions.ordinal = false;
  4416. axis.lin2val = BrokenAxisAdditions.lin2Val;
  4417. axis.val2lin = BrokenAxisAdditions.val2Lin;
  4418. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  4419. // If trying to set extremes inside a break, extend min to
  4420. // after, and max to before the break ( #3857 )
  4421. if (brokenAxis.hasBreaks) {
  4422. var axisBreak = void 0,
  4423. breaks_1 = this.options.breaks;
  4424. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks_1))) {
  4425. newMin = axisBreak.to;
  4426. }
  4427. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks_1))) {
  4428. newMax = axisBreak.from;
  4429. }
  4430. // If both min and max is within the same break.
  4431. if (newMax < newMin) {
  4432. newMax = newMin;
  4433. }
  4434. }
  4435. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  4436. };
  4437. axis.setAxisTranslation = function () {
  4438. Axis.prototype.setAxisTranslation.call(this);
  4439. brokenAxis.unitLength = void 0;
  4440. if (brokenAxis.hasBreaks) {
  4441. var breaks_2 = axis.options.breaks || [],
  4442. // Temporary one:
  4443. breakArrayT_1 = [],
  4444. breakArray_1 = [],
  4445. length_1 = 0,
  4446. inBrk_1,
  4447. repeat_1,
  4448. min_1 = axis.userMin || axis.min,
  4449. max_1 = axis.userMax || axis.max,
  4450. pointRangePadding = pick(axis.pointRangePadding, 0),
  4451. start_1,
  4452. i_1;
  4453. // Min & max check (#4247)
  4454. breaks_2.forEach(function (brk) {
  4455. repeat_1 = brk.repeat || Infinity;
  4456. if (isNumber(min_1) && isNumber(max_1)) {
  4457. if (BrokenAxisAdditions.isInBreak(brk, min_1)) {
  4458. min_1 += (brk.to % repeat_1) - (min_1 % repeat_1);
  4459. }
  4460. if (BrokenAxisAdditions.isInBreak(brk, max_1)) {
  4461. max_1 -= (max_1 % repeat_1) - (brk.from % repeat_1);
  4462. }
  4463. }
  4464. });
  4465. // Construct an array holding all breaks in the axis
  4466. breaks_2.forEach(function (brk) {
  4467. start_1 = brk.from;
  4468. repeat_1 = brk.repeat || Infinity;
  4469. if (isNumber(min_1) && isNumber(max_1)) {
  4470. while (start_1 - repeat_1 > min_1) {
  4471. start_1 -= repeat_1;
  4472. }
  4473. while (start_1 < min_1) {
  4474. start_1 += repeat_1;
  4475. }
  4476. for (i_1 = start_1; i_1 < max_1; i_1 += repeat_1) {
  4477. breakArrayT_1.push({
  4478. value: i_1,
  4479. move: 'in'
  4480. });
  4481. breakArrayT_1.push({
  4482. value: i_1 + brk.to - brk.from,
  4483. move: 'out',
  4484. size: brk.breakSize
  4485. });
  4486. }
  4487. }
  4488. });
  4489. breakArrayT_1.sort(function (a, b) {
  4490. return ((a.value === b.value) ?
  4491. ((a.move === 'in' ? 0 : 1) -
  4492. (b.move === 'in' ? 0 : 1)) :
  4493. a.value - b.value);
  4494. });
  4495. // Simplify the breaks
  4496. inBrk_1 = 0;
  4497. start_1 = min_1;
  4498. breakArrayT_1.forEach(function (brk) {
  4499. inBrk_1 += (brk.move === 'in' ? 1 : -1);
  4500. if (inBrk_1 === 1 && brk.move === 'in') {
  4501. start_1 = brk.value;
  4502. }
  4503. if (inBrk_1 === 0 && isNumber(start_1)) {
  4504. breakArray_1.push({
  4505. from: start_1,
  4506. to: brk.value,
  4507. len: brk.value - start_1 - (brk.size || 0)
  4508. });
  4509. length_1 += brk.value - start_1 - (brk.size || 0);
  4510. }
  4511. });
  4512. brokenAxis.breakArray = breakArray_1;
  4513. // Used with staticScale, and below the actual axis length,
  4514. // when breaks are substracted.
  4515. if (isNumber(min_1) && isNumber(max_1) && isNumber(axis.min)) {
  4516. brokenAxis.unitLength = max_1 - min_1 - length_1 +
  4517. pointRangePadding;
  4518. fireEvent(axis, 'afterBreaks');
  4519. if (axis.staticScale) {
  4520. axis.transA = axis.staticScale;
  4521. }
  4522. else if (brokenAxis.unitLength) {
  4523. axis.transA *=
  4524. (max_1 - axis.min + pointRangePadding) /
  4525. brokenAxis.unitLength;
  4526. }
  4527. if (pointRangePadding) {
  4528. axis.minPixelPadding =
  4529. axis.transA * (axis.minPointOffset || 0);
  4530. }
  4531. axis.min = min_1;
  4532. axis.max = max_1;
  4533. }
  4534. }
  4535. };
  4536. }
  4537. if (pick(redraw, true)) {
  4538. axis.chart.redraw();
  4539. }
  4540. };
  4541. return BrokenAxisAdditions;
  4542. }());
  4543. /**
  4544. * Axis with support of broken data rows.
  4545. * @private
  4546. * @class
  4547. */
  4548. var BrokenAxis = /** @class */ (function () {
  4549. function BrokenAxis() {
  4550. }
  4551. /**
  4552. * Adds support for broken axes.
  4553. * @private
  4554. */
  4555. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  4556. AxisClass.keepProps.push('brokenAxis');
  4557. var seriesProto = Series.prototype;
  4558. /**
  4559. * @private
  4560. */
  4561. seriesProto.drawBreaks = function (axis, keys) {
  4562. var series = this,
  4563. points = series.points,
  4564. breaks,
  4565. threshold,
  4566. eventName,
  4567. y;
  4568. if (axis && // #5950
  4569. axis.brokenAxis &&
  4570. axis.brokenAxis.hasBreaks) {
  4571. var brokenAxis_1 = axis.brokenAxis;
  4572. keys.forEach(function (key) {
  4573. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  4574. threshold = axis.isXAxis ?
  4575. axis.min :
  4576. pick(series.options.threshold, axis.min);
  4577. points.forEach(function (point) {
  4578. y = pick(point['stack' + key.toUpperCase()], point[key]);
  4579. breaks.forEach(function (brk) {
  4580. if (isNumber(threshold) && isNumber(y)) {
  4581. eventName = false;
  4582. if ((threshold < brk.from && y > brk.to) ||
  4583. (threshold > brk.from && y < brk.from)) {
  4584. eventName = 'pointBreak';
  4585. }
  4586. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  4587. (threshold > brk.from && y > brk.to && y < brk.from)) {
  4588. eventName = 'pointInBreak';
  4589. }
  4590. if (eventName) {
  4591. fireEvent(axis, eventName, { point: point, brk: brk });
  4592. }
  4593. }
  4594. });
  4595. });
  4596. });
  4597. }
  4598. };
  4599. /**
  4600. * Extend getGraphPath by identifying gaps in the data so that we can
  4601. * draw a gap in the line or area. This was moved from ordinal axis
  4602. * module to broken axis module as of #5045.
  4603. *
  4604. * @private
  4605. * @function Highcharts.Series#gappedPath
  4606. *
  4607. * @return {Highcharts.SVGPathArray}
  4608. * Gapped path
  4609. */
  4610. seriesProto.gappedPath = function () {
  4611. var currentDataGrouping = this.currentDataGrouping,
  4612. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  4613. gapSize = this.options.gapSize,
  4614. points = this.points.slice(),
  4615. i = points.length - 1,
  4616. yAxis = this.yAxis,
  4617. stack;
  4618. /**
  4619. * Defines when to display a gap in the graph, together with the
  4620. * [gapUnit](plotOptions.series.gapUnit) option.
  4621. *
  4622. * In case when `dataGrouping` is enabled, points can be grouped
  4623. * into a larger time span. This can make the grouped points to have
  4624. * a greater distance than the absolute value of `gapSize` property,
  4625. * which will result in disappearing graph completely. To prevent
  4626. * this situation the mentioned distance between grouped points is
  4627. * used instead of previously defined `gapSize`.
  4628. *
  4629. * In practice, this option is most often used to visualize gaps in
  4630. * time series. In a stock chart, intraday data is available for
  4631. * daytime hours, while gaps will appear in nights and weekends.
  4632. *
  4633. * @see [gapUnit](plotOptions.series.gapUnit)
  4634. * @see [xAxis.breaks](#xAxis.breaks)
  4635. *
  4636. * @sample {highstock} stock/plotoptions/series-gapsize/
  4637. * Setting the gap size to 2 introduces gaps for weekends
  4638. * in daily datasets.
  4639. *
  4640. * @type {number}
  4641. * @default 0
  4642. * @product highstock
  4643. * @requires modules/broken-axis
  4644. * @apioption plotOptions.series.gapSize
  4645. */
  4646. /**
  4647. * Together with [gapSize](plotOptions.series.gapSize), this option
  4648. * defines where to draw gaps in the graph.
  4649. *
  4650. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  4651. * means that if the distance between two points is greater than
  4652. * 5 times that of the two closest points, the graph will be broken.
  4653. *
  4654. * When the `gapUnit` is `"value"`, the gap is based on absolute
  4655. * axis values, which on a datetime axis is milliseconds. This also
  4656. * applies to the navigator series that inherits gap options from
  4657. * the base series.
  4658. *
  4659. * @see [gapSize](plotOptions.series.gapSize)
  4660. *
  4661. * @type {string}
  4662. * @default relative
  4663. * @since 5.0.13
  4664. * @product highstock
  4665. * @validvalue ["relative", "value"]
  4666. * @requires modules/broken-axis
  4667. * @apioption plotOptions.series.gapUnit
  4668. */
  4669. if (gapSize && i > 0) { // #5008
  4670. // Gap unit is relative
  4671. if (this.options.gapUnit !== 'value') {
  4672. gapSize *= this.basePointRange;
  4673. }
  4674. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  4675. if (groupingSize &&
  4676. groupingSize > gapSize &&
  4677. // Except when DG is forced (e.g. from other series)
  4678. // and has lower granularity than actual points (#11351)
  4679. groupingSize >= this.basePointRange) {
  4680. gapSize = groupingSize;
  4681. }
  4682. // extension for ordinal breaks
  4683. var current = void 0,
  4684. next = void 0;
  4685. while (i--) {
  4686. // Reassign next if it is not visible
  4687. if (!(next && next.visible !== false)) {
  4688. next = points[i + 1];
  4689. }
  4690. current = points[i];
  4691. // Skip iteration if one of the points is not visible
  4692. if (next.visible === false || current.visible === false) {
  4693. continue;
  4694. }
  4695. if (next.x - current.x > gapSize) {
  4696. var xRange = (current.x + next.x) / 2;
  4697. points.splice(// insert after this one
  4698. i + 1, 0, {
  4699. isNull: true,
  4700. x: xRange
  4701. });
  4702. // For stacked chart generate empty stack items, #6546
  4703. if (yAxis.stacking && this.options.stacking) {
  4704. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  4705. new StackItem(yAxis, yAxis.options
  4706. .stackLabels, false, xRange, this.stack);
  4707. stack.total = 0;
  4708. }
  4709. }
  4710. // Assign current to next for the upcoming iteration
  4711. next = current;
  4712. }
  4713. }
  4714. // Call base method
  4715. return this.getGraphPath(points);
  4716. };
  4717. /* eslint-disable no-invalid-this */
  4718. addEvent(AxisClass, 'init', function () {
  4719. var axis = this;
  4720. if (!axis.brokenAxis) {
  4721. axis.brokenAxis = new BrokenAxisAdditions(axis);
  4722. }
  4723. });
  4724. addEvent(AxisClass, 'afterInit', function () {
  4725. if (typeof this.brokenAxis !== 'undefined') {
  4726. this.brokenAxis.setBreaks(this.options.breaks, false);
  4727. }
  4728. });
  4729. addEvent(AxisClass, 'afterSetTickPositions', function () {
  4730. var axis = this;
  4731. var brokenAxis = axis.brokenAxis;
  4732. if (brokenAxis &&
  4733. brokenAxis.hasBreaks) {
  4734. var tickPositions = this.tickPositions,
  4735. info = this.tickPositions.info,
  4736. newPositions = [],
  4737. i = void 0;
  4738. for (i = 0; i < tickPositions.length; i++) {
  4739. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  4740. newPositions.push(tickPositions[i]);
  4741. }
  4742. }
  4743. this.tickPositions = newPositions;
  4744. this.tickPositions.info = info;
  4745. }
  4746. });
  4747. // Force Axis to be not-ordinal when breaks are defined
  4748. addEvent(AxisClass, 'afterSetOptions', function () {
  4749. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  4750. this.options.ordinal = false;
  4751. }
  4752. });
  4753. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  4754. var _a = this,
  4755. isDirty = _a.isDirty,
  4756. connectNulls = _a.options.connectNulls,
  4757. points = _a.points,
  4758. xAxis = _a.xAxis,
  4759. yAxis = _a.yAxis;
  4760. // Set, or reset visibility of the points. Axis.setBreaks marks the
  4761. // series as isDirty
  4762. if (isDirty) {
  4763. var i = points.length;
  4764. while (i--) {
  4765. var point = points[i];
  4766. // Respect nulls inside the break (#4275)
  4767. var nullGap = point.y === null && connectNulls === false;
  4768. var isPointInBreak = (!nullGap && ((xAxis &&
  4769. xAxis.brokenAxis &&
  4770. xAxis.brokenAxis.isInAnyBreak(point.x,
  4771. true)) || (yAxis &&
  4772. yAxis.brokenAxis &&
  4773. yAxis.brokenAxis.isInAnyBreak(point.y,
  4774. true))));
  4775. // Set point.visible if in any break.
  4776. // If not in break, reset visible to original value.
  4777. point.visible = isPointInBreak ?
  4778. false :
  4779. point.options.visible !== false;
  4780. }
  4781. }
  4782. });
  4783. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  4784. this.drawBreaks(this.xAxis, ['x']);
  4785. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  4786. });
  4787. };
  4788. return BrokenAxis;
  4789. }());
  4790. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  4791. return BrokenAxis;
  4792. });
  4793. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  4794. });
  4795. _registerModule(_modules, 'Extensions/DataGrouping.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/FormatUtilities.js'], _modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Tooltip.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js']], function (Axis, DateTimeAxis, F, H, Point, Series, Tooltip, O, U) {
  4796. /* *
  4797. *
  4798. * (c) 2010-2021 Torstein Honsi
  4799. *
  4800. * License: www.highcharts.com/license
  4801. *
  4802. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4803. *
  4804. * */
  4805. var format = F.format;
  4806. var seriesProto = Series.prototype;
  4807. var addEvent = U.addEvent,
  4808. arrayMax = U.arrayMax,
  4809. arrayMin = U.arrayMin,
  4810. correctFloat = U.correctFloat,
  4811. defined = U.defined,
  4812. error = U.error,
  4813. extend = U.extend,
  4814. isNumber = U.isNumber,
  4815. merge = U.merge,
  4816. pick = U.pick;
  4817. /**
  4818. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  4819. */
  4820. /**
  4821. * The position of the point inside the group.
  4822. *
  4823. * @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
  4824. */
  4825. /**
  4826. * The position of the first or last point in the series inside the group.
  4827. *
  4828. * @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
  4829. */
  4830. /**
  4831. * @interface Highcharts.DataGroupingInfoObject
  4832. */ /**
  4833. * @name Highcharts.DataGroupingInfoObject#length
  4834. * @type {number}
  4835. */ /**
  4836. * @name Highcharts.DataGroupingInfoObject#options
  4837. * @type {Highcharts.SeriesOptionsType|undefined}
  4838. */ /**
  4839. * @name Highcharts.DataGroupingInfoObject#start
  4840. * @type {number}
  4841. */
  4842. ''; // detach doclets above
  4843. /* ************************************************************************** *
  4844. * Start data grouping module *
  4845. * ************************************************************************** */
  4846. /* eslint-disable no-invalid-this, valid-jsdoc */
  4847. /**
  4848. * Define the available approximation types. The data grouping
  4849. * approximations takes an array or numbers as the first parameter. In case
  4850. * of ohlc, four arrays are sent in as four parameters. Each array consists
  4851. * only of numbers. In case null values belong to the group, the property
  4852. * .hasNulls will be set to true on the array.
  4853. *
  4854. * @product highstock
  4855. *
  4856. * @private
  4857. * @name Highcharts.approximations
  4858. * @type {Highcharts.Dictionary<Function>}
  4859. */
  4860. var approximations = H.approximations = {
  4861. sum: function (arr) {
  4862. var len = arr.length,
  4863. ret;
  4864. // 1. it consists of nulls exclusive
  4865. if (!len && arr.hasNulls) {
  4866. ret = null;
  4867. // 2. it has a length and real values
  4868. }
  4869. else if (len) {
  4870. ret = 0;
  4871. while (len--) {
  4872. ret += arr[len];
  4873. }
  4874. }
  4875. // 3. it has zero length, so just return undefined
  4876. // => doNothing()
  4877. return ret;
  4878. },
  4879. average: function (arr) {
  4880. var len = arr.length,
  4881. ret = approximations.sum(arr);
  4882. // If we have a number, return it divided by the length. If not,
  4883. // return null or undefined based on what the sum method finds.
  4884. if (isNumber(ret) && len) {
  4885. ret = correctFloat(ret / len);
  4886. }
  4887. return ret;
  4888. },
  4889. // The same as average, but for series with multiple values, like area
  4890. // ranges.
  4891. averages: function () {
  4892. var ret = [];
  4893. [].forEach.call(arguments, function (arr) {
  4894. ret.push(approximations.average(arr));
  4895. });
  4896. // Return undefined when first elem. is undefined and let
  4897. // sum method handle null (#7377)
  4898. return typeof ret[0] === 'undefined' ? void 0 : ret;
  4899. },
  4900. open: function (arr) {
  4901. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  4902. },
  4903. high: function (arr) {
  4904. return arr.length ?
  4905. arrayMax(arr) :
  4906. (arr.hasNulls ? null : void 0);
  4907. },
  4908. low: function (arr) {
  4909. return arr.length ?
  4910. arrayMin(arr) :
  4911. (arr.hasNulls ? null : void 0);
  4912. },
  4913. close: function (arr) {
  4914. return arr.length ?
  4915. arr[arr.length - 1] :
  4916. (arr.hasNulls ? null : void 0);
  4917. },
  4918. // ohlc and range are special cases where a multidimensional array is
  4919. // input and an array is output
  4920. ohlc: function (open, high, low, close) {
  4921. open = approximations.open(open);
  4922. high = approximations.high(high);
  4923. low = approximations.low(low);
  4924. close = approximations.close(close);
  4925. if (isNumber(open) ||
  4926. isNumber(high) ||
  4927. isNumber(low) ||
  4928. isNumber(close)) {
  4929. return [open, high, low, close];
  4930. }
  4931. // else, return is undefined
  4932. },
  4933. range: function (low, high) {
  4934. low = approximations.low(low);
  4935. high = approximations.high(high);
  4936. if (isNumber(low) || isNumber(high)) {
  4937. return [low, high];
  4938. }
  4939. if (low === null && high === null) {
  4940. return null;
  4941. }
  4942. // else, return is undefined
  4943. }
  4944. };
  4945. var groupData = function (xData,
  4946. yData,
  4947. groupPositions,
  4948. approximation) {
  4949. var series = this,
  4950. data = series.data,
  4951. dataOptions = series.options && series.options.data,
  4952. groupedXData = [],
  4953. groupedYData = [],
  4954. groupMap = [],
  4955. dataLength = xData.length,
  4956. pointX,
  4957. pointY,
  4958. groupedY,
  4959. // when grouping the fake extended axis for panning,
  4960. // we don't need to consider y
  4961. handleYData = !!yData,
  4962. values = [],
  4963. approximationFn,
  4964. pointArrayMap = series.pointArrayMap,
  4965. pointArrayMapLength = pointArrayMap && pointArrayMap.length,
  4966. extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']),
  4967. groupAll = this.options.dataGrouping && this.options.dataGrouping.groupAll,
  4968. pos = 0,
  4969. start = 0,
  4970. valuesLen,
  4971. i,
  4972. j;
  4973. /**
  4974. * @private
  4975. */
  4976. function getApproximation(approx) {
  4977. if (typeof approx === 'function') {
  4978. return approx;
  4979. }
  4980. if (approximations[approx]) {
  4981. return approximations[approx];
  4982. }
  4983. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  4984. 'average'];
  4985. }
  4986. approximationFn = getApproximation(approximation);
  4987. // Calculate values array size from pointArrayMap length
  4988. if (pointArrayMapLength) {
  4989. pointArrayMap.forEach(function () {
  4990. values.push([]);
  4991. });
  4992. }
  4993. else {
  4994. values.push([]);
  4995. }
  4996. valuesLen = pointArrayMapLength || 1;
  4997. // Start with the first point within the X axis range (#2696)
  4998. for (i = 0; i <= dataLength; i++) {
  4999. if (xData[i] >= groupPositions[0]) {
  5000. break;
  5001. }
  5002. }
  5003. for (i; i <= dataLength; i++) {
  5004. // when a new group is entered, summarize and initialize
  5005. // the previous group
  5006. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  5007. xData[i] >= groupPositions[pos + 1]) ||
  5008. i === dataLength) { // get the last group
  5009. // get group x and y
  5010. pointX = groupPositions[pos];
  5011. series.dataGroupInfo = {
  5012. start: groupAll ? start : (series.cropStart + start),
  5013. length: values[0].length
  5014. };
  5015. groupedY = approximationFn.apply(series, values);
  5016. // By default, let options of the first grouped point be passed over
  5017. // to the grouped point. This allows preserving properties like
  5018. // `name` and `color` or custom properties. Implementers can
  5019. // override this from the approximation function, where they can
  5020. // write custom options to `this.dataGroupInfo.options`.
  5021. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  5022. // Convert numbers and arrays into objects
  5023. series.dataGroupInfo.options = merge(series.pointClass.prototype
  5024. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  5025. // Make sure the raw data (x, y, open, high etc) is not copied
  5026. // over and overwriting approximated data.
  5027. extendedPointArrayMap.forEach(function (key) {
  5028. delete series.dataGroupInfo.options[key];
  5029. });
  5030. }
  5031. // push the grouped data
  5032. if (typeof groupedY !== 'undefined') {
  5033. groupedXData.push(pointX);
  5034. groupedYData.push(groupedY);
  5035. groupMap.push(series.dataGroupInfo);
  5036. }
  5037. // reset the aggregate arrays
  5038. start = i;
  5039. for (j = 0; j < valuesLen; j++) {
  5040. values[j].length = 0; // faster than values[j] = []
  5041. values[j].hasNulls = false;
  5042. }
  5043. // Advance on the group positions
  5044. pos += 1;
  5045. // don't loop beyond the last group
  5046. if (i === dataLength) {
  5047. break;
  5048. }
  5049. }
  5050. // break out
  5051. if (i === dataLength) {
  5052. break;
  5053. }
  5054. // for each raw data point, push it to an array that contains all values
  5055. // for this specific group
  5056. if (pointArrayMap) {
  5057. var index = (series.options.dataGrouping &&
  5058. series.options.dataGrouping.groupAll ?
  5059. i : series.cropStart + i),
  5060. point = (data && data[index]) ||
  5061. series.pointClass.prototype.applyOptions.apply({
  5062. series: series
  5063. },
  5064. [dataOptions[index]]),
  5065. val = void 0;
  5066. for (j = 0; j < pointArrayMapLength; j++) {
  5067. val = point[pointArrayMap[j]];
  5068. if (isNumber(val)) {
  5069. values[j].push(val);
  5070. }
  5071. else if (val === null) {
  5072. values[j].hasNulls = true;
  5073. }
  5074. }
  5075. }
  5076. else {
  5077. pointY = handleYData ? yData[i] : null;
  5078. if (isNumber(pointY)) {
  5079. values[0].push(pointY);
  5080. }
  5081. else if (pointY === null) {
  5082. values[0].hasNulls = true;
  5083. }
  5084. }
  5085. }
  5086. return {
  5087. groupedXData: groupedXData,
  5088. groupedYData: groupedYData,
  5089. groupMap: groupMap
  5090. };
  5091. };
  5092. var anchorPoints = function (series,
  5093. groupedXData,
  5094. xMax) {
  5095. var options = series.options,
  5096. dataGroupingOptions = options.dataGrouping,
  5097. totalRange = series.currentDataGrouping && series.currentDataGrouping.gapSize;
  5098. var i;
  5099. // DataGrouping x-coordinates.
  5100. if (dataGroupingOptions && series.xData && totalRange && series.groupMap) {
  5101. var groupedDataLength = groupedXData.length - 1,
  5102. anchor = dataGroupingOptions.anchor,
  5103. firstAnchor = pick(dataGroupingOptions.firstAnchor,
  5104. anchor),
  5105. lastAnchor = pick(dataGroupingOptions.lastAnchor,
  5106. anchor);
  5107. // Anchor points that are not extremes.
  5108. if (anchor && anchor !== 'start') {
  5109. var shiftInterval = totalRange *
  5110. { middle: 0.5,
  5111. end: 1 }[anchor];
  5112. i = groupedXData.length - 1;
  5113. while (i-- && i > 0) {
  5114. groupedXData[i] += shiftInterval;
  5115. }
  5116. }
  5117. // Change the first point position, but only when it is
  5118. // the first point in the data set not in the current zoom.
  5119. if (firstAnchor &&
  5120. firstAnchor !== 'start' &&
  5121. series.xData[0] >= groupedXData[0]) {
  5122. var groupStart = series.groupMap[0].start,
  5123. groupLength = series.groupMap[0].length;
  5124. var firstGroupstEnd = void 0;
  5125. if (isNumber(groupStart) && isNumber(groupLength)) {
  5126. firstGroupstEnd = groupStart + (groupLength - 1);
  5127. }
  5128. groupedXData[0] = {
  5129. middle: groupedXData[0] + 0.5 * totalRange,
  5130. end: groupedXData[0] + totalRange,
  5131. firstPoint: series.xData[0],
  5132. lastPoint: firstGroupstEnd && series.xData[firstGroupstEnd]
  5133. }[firstAnchor];
  5134. }
  5135. // Change the last point position but only when it is
  5136. // the last point in the data set not in the current zoom.
  5137. if (lastAnchor &&
  5138. lastAnchor !== 'start' &&
  5139. totalRange &&
  5140. groupedXData[groupedDataLength] >= xMax - totalRange) {
  5141. var lastGroupStart = series.groupMap[series.groupMap.length - 1].start;
  5142. groupedXData[groupedDataLength] = {
  5143. middle: groupedXData[groupedDataLength] + 0.5 * totalRange,
  5144. end: groupedXData[groupedDataLength] + totalRange,
  5145. firstPoint: lastGroupStart && series.xData[lastGroupStart],
  5146. lastPoint: series.xData[series.xData.length - 1]
  5147. }[lastAnchor];
  5148. }
  5149. }
  5150. };
  5151. var adjustExtremes = function (xAxis,
  5152. groupedXData) {
  5153. // Make sure the X axis extends to show the first group (#2533)
  5154. // But only for visible series (#5493, #6393)
  5155. if (defined(groupedXData[0]) &&
  5156. isNumber(xAxis.min) &&
  5157. isNumber(xAxis.dataMin) &&
  5158. groupedXData[0] < xAxis.min) {
  5159. if ((!defined(xAxis.options.min) &&
  5160. xAxis.min <= xAxis.dataMin) ||
  5161. xAxis.min === xAxis.dataMin) {
  5162. xAxis.min = Math.min(groupedXData[0],
  5163. xAxis.min);
  5164. }
  5165. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  5166. }
  5167. // When the last anchor set, change the extremes that
  5168. // the last point is visible (#12455).
  5169. if (defined(groupedXData[groupedXData.length - 1]) &&
  5170. isNumber(xAxis.max) &&
  5171. isNumber(xAxis.dataMax) &&
  5172. groupedXData[groupedXData.length - 1] > xAxis.max) {
  5173. if ((!defined(xAxis.options.max) &&
  5174. isNumber(xAxis.dataMax) &&
  5175. xAxis.max >= xAxis.dataMax) || xAxis.max === xAxis.dataMax) {
  5176. xAxis.max = Math.max(groupedXData[groupedXData.length - 1], xAxis.max);
  5177. }
  5178. xAxis.dataMax = Math.max(groupedXData[groupedXData.length - 1], xAxis.dataMax);
  5179. }
  5180. };
  5181. var dataGrouping = {
  5182. approximations: approximations,
  5183. groupData: groupData
  5184. };
  5185. // -----------------------------------------------------------------------------
  5186. // The following code applies to implementation of data grouping on a Series
  5187. var baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  5188. /** @ignore */
  5189. commonOptions = {
  5190. // enabled: null, // (true for stock charts, false for basic),
  5191. // forced: undefined,
  5192. groupPixelWidth: 2,
  5193. // the first one is the point or start value, the second is the start
  5194. // value if we're dealing with range, the third one is the end value if
  5195. // dealing with a range
  5196. dateTimeLabelFormats: {
  5197. millisecond: [
  5198. '%A, %b %e, %H:%M:%S.%L',
  5199. '%A, %b %e, %H:%M:%S.%L',
  5200. '-%H:%M:%S.%L'
  5201. ],
  5202. second: [
  5203. '%A, %b %e, %H:%M:%S',
  5204. '%A, %b %e, %H:%M:%S',
  5205. '-%H:%M:%S'
  5206. ],
  5207. minute: [
  5208. '%A, %b %e, %H:%M',
  5209. '%A, %b %e, %H:%M',
  5210. '-%H:%M'
  5211. ],
  5212. hour: [
  5213. '%A, %b %e, %H:%M',
  5214. '%A, %b %e, %H:%M',
  5215. '-%H:%M'
  5216. ],
  5217. day: [
  5218. '%A, %b %e, %Y',
  5219. '%A, %b %e',
  5220. '-%A, %b %e, %Y'
  5221. ],
  5222. week: [
  5223. 'Week from %A, %b %e, %Y',
  5224. '%A, %b %e',
  5225. '-%A, %b %e, %Y'
  5226. ],
  5227. month: [
  5228. '%B %Y',
  5229. '%B',
  5230. '-%B %Y'
  5231. ],
  5232. year: [
  5233. '%Y',
  5234. '%Y',
  5235. '-%Y'
  5236. ]
  5237. }
  5238. // smoothed = false, // enable this for navigator series only
  5239. }, specificOptions = {
  5240. line: {},
  5241. spline: {},
  5242. area: {},
  5243. areaspline: {},
  5244. arearange: {},
  5245. column: {
  5246. groupPixelWidth: 10
  5247. },
  5248. columnrange: {
  5249. groupPixelWidth: 10
  5250. },
  5251. candlestick: {
  5252. groupPixelWidth: 10
  5253. },
  5254. ohlc: {
  5255. groupPixelWidth: 5
  5256. }
  5257. },
  5258. // units are defined in a separate array to allow complete overriding in
  5259. // case of a user option
  5260. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  5261. [
  5262. 'millisecond',
  5263. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5264. ], [
  5265. 'second',
  5266. [1, 2, 5, 10, 15, 30]
  5267. ], [
  5268. 'minute',
  5269. [1, 2, 5, 10, 15, 30]
  5270. ], [
  5271. 'hour',
  5272. [1, 2, 3, 4, 6, 8, 12]
  5273. ], [
  5274. 'day',
  5275. [1]
  5276. ], [
  5277. 'week',
  5278. [1]
  5279. ], [
  5280. 'month',
  5281. [1, 3, 6]
  5282. ], [
  5283. 'year',
  5284. null
  5285. ]
  5286. ];
  5287. // Set default approximations to the prototypes if present. Properties are
  5288. // inherited down. Can be overridden for individual series types.
  5289. seriesProto.getDGApproximation = function () {
  5290. if (this.is('arearange')) {
  5291. return 'range';
  5292. }
  5293. if (this.is('ohlc')) {
  5294. return 'ohlc';
  5295. }
  5296. if (this.is('column')) {
  5297. return 'sum';
  5298. }
  5299. return 'average';
  5300. };
  5301. /**
  5302. * Takes parallel arrays of x and y data and groups the data into intervals
  5303. * defined by groupPositions, a collection of starting x values for each group.
  5304. *
  5305. * @private
  5306. * @function Highcharts.Series#groupData
  5307. *
  5308. * @param {Array<number>} xData
  5309. *
  5310. * @param {Array<number>|Array<Array<number>>} yData
  5311. *
  5312. * @param {boolean} groupPositions
  5313. *
  5314. * @param {string|Function} approximation
  5315. *
  5316. * @return {void}
  5317. */
  5318. seriesProto.groupData = groupData;
  5319. // Extend the basic processData method, that crops the data to the current zoom
  5320. // range, with data grouping logic.
  5321. seriesProto.processData = function () {
  5322. var series = this,
  5323. chart = series.chart,
  5324. options = series.options,
  5325. dataGroupingOptions = options.dataGrouping,
  5326. groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  5327. pick(dataGroupingOptions.enabled,
  5328. chart.options.isStock),
  5329. visible = (series.visible || !chart.options.chart.ignoreHiddenSeries),
  5330. hasGroupedData,
  5331. skip,
  5332. lastDataGrouping = this.currentDataGrouping,
  5333. currentDataGrouping,
  5334. croppedData,
  5335. revertRequireSorting = false;
  5336. // Run base method
  5337. series.forceCrop = groupingEnabled; // #334
  5338. series.groupPixelWidth = null; // #2110
  5339. series.hasProcessed = true; // #2692
  5340. // Data needs to be sorted for dataGrouping
  5341. if (groupingEnabled && !series.requireSorting) {
  5342. series.requireSorting = revertRequireSorting = true;
  5343. }
  5344. // Skip if processData returns false or if grouping is disabled (in that
  5345. // order)
  5346. skip = (baseProcessData.apply(series, arguments) === false ||
  5347. !groupingEnabled);
  5348. // Revert original requireSorting value if changed
  5349. if (revertRequireSorting) {
  5350. series.requireSorting = false;
  5351. }
  5352. if (!skip) {
  5353. series.destroyGroupedData();
  5354. var i = void 0,
  5355. processedXData = dataGroupingOptions.groupAll ?
  5356. series.xData :
  5357. series.processedXData,
  5358. processedYData = dataGroupingOptions.groupAll ?
  5359. series.yData :
  5360. series.processedYData,
  5361. plotSizeX = chart.plotSizeX,
  5362. xAxis = series.xAxis,
  5363. ordinal = xAxis.options.ordinal,
  5364. groupPixelWidth = series.groupPixelWidth =
  5365. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  5366. // Execute grouping if the amount of points is greater than the limit
  5367. // defined in groupPixelWidth
  5368. if (groupPixelWidth &&
  5369. processedXData &&
  5370. processedXData.length) {
  5371. hasGroupedData = true;
  5372. // Force recreation of point instances in series.translate, #5699
  5373. series.isDirty = true;
  5374. series.points = null; // #6709
  5375. var extremes = xAxis.getExtremes(),
  5376. xMin = extremes.min,
  5377. xMax = extremes.max,
  5378. groupIntervalFactor = (ordinal &&
  5379. xAxis.ordinal &&
  5380. xAxis.ordinal.getGroupIntervalFactor(xMin,
  5381. xMax,
  5382. series)) || 1,
  5383. interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  5384. groupIntervalFactor,
  5385. groupPositions = xAxis.getTimeTicks(DateTimeAxis.AdditionsClass.prototype.normalizeTimeTickInterval(interval,
  5386. dataGroupingOptions.units ||
  5387. defaultDataGroupingUnits),
  5388. // Processed data may extend beyond axis (#4907)
  5389. Math.min(xMin,
  5390. processedXData[0]),
  5391. Math.max(xMax,
  5392. processedXData[processedXData.length - 1]),
  5393. xAxis.options.startOfWeek,
  5394. processedXData,
  5395. series.closestPointRange),
  5396. groupedData = seriesProto.groupData.apply(series,
  5397. [
  5398. processedXData,
  5399. processedYData,
  5400. groupPositions,
  5401. dataGroupingOptions.approximation
  5402. ]),
  5403. groupedXData = groupedData.groupedXData,
  5404. groupedYData = groupedData.groupedYData,
  5405. gapSize = 0;
  5406. // The smoothed option is deprecated, instead,
  5407. // there is a fallback to the new anchoring mechanism. #12455.
  5408. if (dataGroupingOptions && dataGroupingOptions.smoothed && groupedXData.length) {
  5409. dataGroupingOptions.firstAnchor = 'firstPoint';
  5410. dataGroupingOptions.anchor = 'middle';
  5411. dataGroupingOptions.lastAnchor = 'lastPoint';
  5412. error(32, false, chart, { 'dataGrouping.smoothed': 'use dataGrouping.anchor' });
  5413. }
  5414. anchorPoints(series, groupedXData, xMax);
  5415. // Record what data grouping values were used
  5416. for (i = 1; i < groupPositions.length; i++) {
  5417. // The grouped gapSize needs to be the largest distance between
  5418. // the group to capture varying group sizes like months or DST
  5419. // crossing (#10000). Also check that the gap is not at the
  5420. // start of a segment.
  5421. if (!groupPositions.info.segmentStarts ||
  5422. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  5423. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  5424. }
  5425. }
  5426. currentDataGrouping = groupPositions.info;
  5427. currentDataGrouping.gapSize = gapSize;
  5428. series.closestPointRange = groupPositions.info.totalRange;
  5429. series.groupMap = groupedData.groupMap;
  5430. if (visible) {
  5431. adjustExtremes(xAxis, groupedXData);
  5432. }
  5433. // We calculated all group positions but we should render
  5434. // only the ones within the visible range
  5435. if (dataGroupingOptions.groupAll) {
  5436. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  5437. );
  5438. groupedXData = croppedData.xData;
  5439. groupedYData = croppedData.yData;
  5440. series.cropStart = croppedData.start; // #15005
  5441. }
  5442. // Set series props
  5443. series.processedXData = groupedXData;
  5444. series.processedYData = groupedYData;
  5445. }
  5446. else {
  5447. series.groupMap = null;
  5448. }
  5449. series.hasGroupedData = hasGroupedData;
  5450. series.currentDataGrouping = currentDataGrouping;
  5451. series.preventGraphAnimation =
  5452. (lastDataGrouping && lastDataGrouping.totalRange) !==
  5453. (currentDataGrouping && currentDataGrouping.totalRange);
  5454. }
  5455. };
  5456. // Destroy the grouped data points. #622, #740
  5457. seriesProto.destroyGroupedData = function () {
  5458. // Clear previous groups
  5459. if (this.groupedData) {
  5460. this.groupedData.forEach(function (point, i) {
  5461. if (point) {
  5462. this.groupedData[i] = point.destroy ?
  5463. point.destroy() : null;
  5464. }
  5465. }, this);
  5466. // Clears all:
  5467. // - `this.groupedData`
  5468. // - `this.points`
  5469. // - `preserve` object in series.update()
  5470. this.groupedData.length = 0;
  5471. }
  5472. };
  5473. // Override the generatePoints method by adding a reference to grouped data
  5474. seriesProto.generatePoints = function () {
  5475. baseGeneratePoints.apply(this);
  5476. // Record grouped data in order to let it be destroyed the next time
  5477. // processData runs
  5478. this.destroyGroupedData(); // #622
  5479. this.groupedData = this.hasGroupedData ? this.points : null;
  5480. };
  5481. // Override point prototype to throw a warning when trying to update grouped
  5482. // points.
  5483. addEvent(Point, 'update', function () {
  5484. if (this.dataGroup) {
  5485. error(24, false, this.series.chart);
  5486. return false;
  5487. }
  5488. });
  5489. // Extend the original method, make the tooltip's header reflect the grouped
  5490. // range.
  5491. addEvent(Tooltip, 'headerFormatter', function (e) {
  5492. var tooltip = this,
  5493. chart = this.chart,
  5494. time = chart.time,
  5495. labelConfig = e.labelConfig,
  5496. series = labelConfig.series,
  5497. options = series.options,
  5498. tooltipOptions = series.tooltipOptions,
  5499. dataGroupingOptions = options.dataGrouping,
  5500. xDateFormat = tooltipOptions.xDateFormat,
  5501. xDateFormatEnd,
  5502. xAxis = series.xAxis,
  5503. currentDataGrouping,
  5504. dateTimeLabelFormats,
  5505. labelFormats,
  5506. formattedKey,
  5507. formatString = tooltipOptions[(e.isFooter ? 'footer' : 'header') + 'Format'];
  5508. // apply only to grouped series
  5509. if (xAxis &&
  5510. xAxis.options.type === 'datetime' &&
  5511. dataGroupingOptions &&
  5512. isNumber(labelConfig.key)) {
  5513. // set variables
  5514. currentDataGrouping = series.currentDataGrouping;
  5515. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  5516. // Fallback to commonOptions (#9693)
  5517. commonOptions.dateTimeLabelFormats;
  5518. // if we have grouped data, use the grouping information to get the
  5519. // right format
  5520. if (currentDataGrouping) {
  5521. labelFormats =
  5522. dateTimeLabelFormats[currentDataGrouping.unitName];
  5523. if (currentDataGrouping.count === 1) {
  5524. xDateFormat = labelFormats[0];
  5525. }
  5526. else {
  5527. xDateFormat = labelFormats[1];
  5528. xDateFormatEnd = labelFormats[2];
  5529. }
  5530. // if not grouped, and we don't have set the xDateFormat option, get the
  5531. // best fit, so if the least distance between points is one minute, show
  5532. // it, but if the least distance is one day, skip hours and minutes etc.
  5533. }
  5534. else if (!xDateFormat && dateTimeLabelFormats) {
  5535. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  5536. }
  5537. // now format the key
  5538. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  5539. if (xDateFormatEnd) {
  5540. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  5541. }
  5542. // Replace default header style with class name
  5543. if (series.chart.styledMode) {
  5544. formatString = this.styledModeFormat(formatString);
  5545. }
  5546. // return the replaced format
  5547. e.text = format(formatString, {
  5548. point: extend(labelConfig.point, { key: formattedKey }),
  5549. series: series
  5550. }, chart);
  5551. e.preventDefault();
  5552. }
  5553. });
  5554. // Destroy grouped data on series destroy
  5555. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  5556. // Handle default options for data grouping. This must be set at runtime because
  5557. // some series types are defined after this.
  5558. addEvent(Series, 'afterSetOptions', function (e) {
  5559. var options = e.options,
  5560. type = this.type,
  5561. plotOptions = this.chart.options.plotOptions,
  5562. defaultOptions = O.defaultOptions.plotOptions[type].dataGrouping,
  5563. // External series, for example technical indicators should also
  5564. // inherit commonOptions which are not available outside this module
  5565. baseOptions = this.useCommonDataGrouping && commonOptions;
  5566. if (specificOptions[type] || baseOptions) { // #1284
  5567. if (!defaultOptions) {
  5568. defaultOptions = merge(commonOptions, specificOptions[type]);
  5569. }
  5570. var rangeSelector = this.chart.rangeSelector;
  5571. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  5572. // Set by the StockChart constructor:
  5573. plotOptions[type].dataGrouping, this.userOptions.dataGrouping, !options.isInternal &&
  5574. rangeSelector &&
  5575. isNumber(rangeSelector.selected) &&
  5576. rangeSelector.buttonOptions[rangeSelector.selected].dataGrouping);
  5577. }
  5578. });
  5579. // When resetting the scale reset the hasProccessed flag to avoid taking
  5580. // previous data grouping of neighbour series into accound when determining
  5581. // group pixel width (#2692).
  5582. addEvent(Axis, 'afterSetScale', function () {
  5583. this.series.forEach(function (series) {
  5584. series.hasProcessed = false;
  5585. });
  5586. });
  5587. // Get the data grouping pixel width based on the greatest defined individual
  5588. // width of the axis' series, and if whether one of the axes need grouping.
  5589. Axis.prototype.getGroupPixelWidth = function () {
  5590. var series = this.series,
  5591. len = series.length,
  5592. i,
  5593. groupPixelWidth = 0,
  5594. doGrouping = false,
  5595. dataLength,
  5596. dgOptions;
  5597. // If multiple series are compared on the same x axis, give them the same
  5598. // group pixel width (#334)
  5599. i = len;
  5600. while (i--) {
  5601. dgOptions = series[i].options.dataGrouping;
  5602. if (dgOptions) {
  5603. groupPixelWidth = Math.max(groupPixelWidth,
  5604. // Fallback to commonOptions (#9693)
  5605. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  5606. }
  5607. }
  5608. // If one of the series needs grouping, apply it to all (#1634)
  5609. i = len;
  5610. while (i--) {
  5611. dgOptions = series[i].options.dataGrouping;
  5612. if (dgOptions && series[i].hasProcessed) { // #2692
  5613. dataLength = (series[i].processedXData || series[i].data).length;
  5614. // Execute grouping if the amount of points is greater than the
  5615. // limit defined in groupPixelWidth
  5616. if (series[i].groupPixelWidth ||
  5617. dataLength >
  5618. (this.chart.plotSizeX / groupPixelWidth) ||
  5619. (dataLength && dgOptions.forced)) {
  5620. doGrouping = true;
  5621. }
  5622. }
  5623. }
  5624. return doGrouping ? groupPixelWidth : 0;
  5625. };
  5626. /**
  5627. * Highcharts Stock only. Force data grouping on all the axis' series.
  5628. *
  5629. * @product highstock
  5630. *
  5631. * @function Highcharts.Axis#setDataGrouping
  5632. *
  5633. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  5634. * A `dataGrouping` configuration. Use `false` to disable data grouping
  5635. * dynamically.
  5636. *
  5637. * @param {boolean} [redraw=true]
  5638. * Whether to redraw the chart or wait for a later call to
  5639. * {@link Chart#redraw}.
  5640. *
  5641. * @return {void}
  5642. */
  5643. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  5644. var axis = this;
  5645. var i;
  5646. redraw = pick(redraw, true);
  5647. if (!dataGrouping) {
  5648. dataGrouping = {
  5649. forced: false,
  5650. units: null
  5651. };
  5652. }
  5653. // Axis is instantiated, update all series
  5654. if (this instanceof Axis) {
  5655. i = this.series.length;
  5656. while (i--) {
  5657. this.series[i].update({
  5658. dataGrouping: dataGrouping
  5659. }, false);
  5660. }
  5661. // Axis not yet instanciated, alter series options
  5662. }
  5663. else {
  5664. this.chart.options.series.forEach(function (seriesOptions) {
  5665. seriesOptions.dataGrouping = dataGrouping;
  5666. }, false);
  5667. }
  5668. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  5669. if (axis.ordinal) {
  5670. axis.ordinal.slope = void 0;
  5671. }
  5672. if (redraw) {
  5673. this.chart.redraw();
  5674. }
  5675. };
  5676. H.dataGrouping = dataGrouping;
  5677. /* eslint-enable no-invalid-this, valid-jsdoc */
  5678. /**
  5679. * Data grouping is the concept of sampling the data values into larger
  5680. * blocks in order to ease readability and increase performance of the
  5681. * JavaScript charts. Highcharts Stock by default applies data grouping when
  5682. * the points become closer than a certain pixel value, determined by
  5683. * the `groupPixelWidth` option.
  5684. *
  5685. * If data grouping is applied, the grouping information of grouped
  5686. * points can be read from the [Point.dataGroup](
  5687. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  5688. * the data itself are set, for example `name` or `color` or custom properties,
  5689. * the grouping logic doesn't know how to group it. In this case the options of
  5690. * the first point instance are copied over to the group point. This can be
  5691. * altered through a custom `approximation` callback function.
  5692. *
  5693. * @declare Highcharts.DataGroupingOptionsObject
  5694. * @product highstock
  5695. * @requires product:highstock
  5696. * @requires module:modules/datagrouping
  5697. * @apioption plotOptions.series.dataGrouping
  5698. */
  5699. /**
  5700. * Specifies how the points should be located on the X axis inside the group.
  5701. * Points that are extremes can be set separately. Available options:
  5702. *
  5703. * - `start` places the point at the beginning of the group
  5704. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5705. *
  5706. * - `middle` places the point in the middle of the group
  5707. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5708. *
  5709. * - `end` places the point at the end of the group
  5710. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5711. *
  5712. * @sample {highstock} stock/plotoptions/series-datagrouping-anchor
  5713. * Changing the point x-coordinate inside the group.
  5714. *
  5715. * @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
  5716. * @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
  5717. *
  5718. * @type {Highcharts.DataGroupingAnchor}
  5719. * @since 9.1.0
  5720. * @default start
  5721. * @apioption plotOptions.series.dataGrouping.anchor
  5722. */
  5723. /**
  5724. * The method of approximation inside a group. When for example 30 days
  5725. * are grouped into one month, this determines what value should represent
  5726. * the group. Possible values are "average", "averages", "open", "high",
  5727. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  5728. * is "ohlc" by default, which finds the open, high, low and close values
  5729. * within all the grouped data. For ranges, the approximation is "range",
  5730. * which finds the low and high values. For multi-dimensional data,
  5731. * like ranges and OHLC, "averages" will compute the average for each
  5732. * dimension.
  5733. *
  5734. * Custom aggregate methods can be added by assigning a callback function
  5735. * as the approximation. This function takes a numeric array as the
  5736. * argument and should return a single numeric value or `null`. Note
  5737. * that the numeric array will never contain null values, only true
  5738. * numbers. Instead, if null values are present in the raw data, the
  5739. * numeric array will have an `.hasNulls` property set to `true`. For
  5740. * single-value data sets the data is available in the first argument
  5741. * of the callback function. For OHLC data sets, all the open values
  5742. * are in the first argument, all high values in the second etc.
  5743. *
  5744. * Since v4.2.7, grouping meta data is available in the approximation
  5745. * callback from `this.dataGroupInfo`. It can be used to extract information
  5746. * from the raw data.
  5747. *
  5748. * Defaults to `average` for line-type series, `sum` for columns, `range`
  5749. * for range series and `ohlc` for OHLC and candlestick.
  5750. *
  5751. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  5752. * Approximation callback with custom data
  5753. * @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
  5754. * Simple approximation demo
  5755. *
  5756. * @type {Highcharts.DataGroupingApproximationValue|Function}
  5757. * @apioption plotOptions.series.dataGrouping.approximation
  5758. */
  5759. /**
  5760. * Datetime formats for the header of the tooltip in a stock chart.
  5761. * The format can vary within a chart depending on the currently selected
  5762. * time range and the current data grouping.
  5763. *
  5764. * The default formats are:
  5765. * ```js
  5766. * {
  5767. * millisecond: [
  5768. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  5769. * ],
  5770. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  5771. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5772. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5773. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5774. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5775. * month: ['%B %Y', '%B', '-%B %Y'],
  5776. * year: ['%Y', '%Y', '-%Y']
  5777. * }
  5778. * ```
  5779. *
  5780. * For each of these array definitions, the first item is the format
  5781. * used when the active time span is one unit. For instance, if the
  5782. * current data applies to one week, the first item of the week array
  5783. * is used. The second and third items are used when the active time
  5784. * span is more than two units. For instance, if the current data applies
  5785. * to two weeks, the second and third item of the week array are used,
  5786. * and applied to the start and end date of the time span.
  5787. *
  5788. * @type {object}
  5789. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  5790. */
  5791. /**
  5792. * Enable or disable data grouping.
  5793. *
  5794. * @type {boolean}
  5795. * @default true
  5796. * @apioption plotOptions.series.dataGrouping.enabled
  5797. */
  5798. /**
  5799. * Specifies how the first grouped point is positioned on the xAxis.
  5800. * If firstAnchor and/or lastAnchor are defined, then those options take
  5801. * precedence over anchor for the first and/or last grouped points.
  5802. * Available options:
  5803. *
  5804. * -`start` places the point at the beginning of the group
  5805. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5806. *
  5807. * -`middle` places the point in the middle of the group
  5808. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5809. *
  5810. * -`end` places the point at the end of the group
  5811. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5812. *
  5813. * -`firstPoint` the first point in the group
  5814. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  5815. *
  5816. * -`lastPoint` the last point in the group
  5817. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  5818. *
  5819. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  5820. * Applying first and last anchor.
  5821. *
  5822. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  5823. *
  5824. * @type {Highcharts.DataGroupingAnchorExtremes}
  5825. * @since 9.1.0
  5826. * @default start
  5827. * @apioption plotOptions.series.dataGrouping.firstAnchor
  5828. */
  5829. /**
  5830. * When data grouping is forced, it runs no matter how small the intervals
  5831. * are. This can be handy for example when the sum should be calculated
  5832. * for values appearing at random times within each hour.
  5833. *
  5834. * @type {boolean}
  5835. * @default false
  5836. * @apioption plotOptions.series.dataGrouping.forced
  5837. */
  5838. /**
  5839. * The approximate pixel width of each group. If for example a series
  5840. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5841. * is performed. If however the series contains so many points that
  5842. * the spacing is less than the groupPixelWidth, Highcharts will try
  5843. * to group it into appropriate groups so that each is more or less
  5844. * two pixels wide. If multiple series with different group pixel widths
  5845. * are drawn on the same x axis, all series will take the greatest width.
  5846. * For example, line series have 2px default group width, while column
  5847. * series have 10px. If combined, both the line and the column will
  5848. * have 10px by default.
  5849. *
  5850. * @type {number}
  5851. * @default 2
  5852. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  5853. */
  5854. /**
  5855. * By default only points within the visible range are grouped. Enabling this
  5856. * option will force data grouping to calculate all grouped points for a given
  5857. * dataset. That option prevents for example a column series from calculating
  5858. * a grouped point partially. The effect is similar to
  5859. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  5860. * not affect yAxis extremes.
  5861. *
  5862. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  5863. * Two series with the same data but different groupAll setting
  5864. *
  5865. * @type {boolean}
  5866. * @default false
  5867. * @since 6.1.0
  5868. * @apioption plotOptions.series.dataGrouping.groupAll
  5869. */
  5870. /**
  5871. * Specifies how the last grouped point is positioned on the xAxis.
  5872. * If firstAnchor and/or lastAnchor are defined, then those options take
  5873. * precedence over anchor for the first and/or last grouped points.
  5874. * Available options:
  5875. *
  5876. * -`start` places the point at the beginning of the group
  5877. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  5878. *
  5879. * -`middle` places the point in the middle of the group
  5880. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  5881. *
  5882. * -`end` places the point at the end of the group
  5883. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  5884. *
  5885. * -`firstPoint` the first point in the group
  5886. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  5887. *
  5888. * -`lastPoint` the last point in the group
  5889. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  5890. *
  5891. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  5892. * Applying first and last anchor.
  5893. *
  5894. * @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
  5895. * Applying the last anchor in the chart with live data.
  5896. *
  5897. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  5898. *
  5899. * @type {Highcharts.DataGroupingAnchorExtremes}
  5900. * @since 9.1.0
  5901. * @default start
  5902. * @apioption plotOptions.series.dataGrouping.lastAnchor
  5903. */
  5904. /**
  5905. * Normally, a group is indexed by the start of that group, so for example
  5906. * when 30 daily values are grouped into one month, that month's x value
  5907. * will be the 1st of the month. This apparently shifts the data to
  5908. * the left. When the smoothed option is true, this is compensated for.
  5909. * The data is shifted to the middle of the group, and min and max
  5910. * values are preserved. Internally, this is used in the Navigator series.
  5911. *
  5912. * @type {boolean}
  5913. * @default false
  5914. * @deprecated
  5915. * @apioption plotOptions.series.dataGrouping.smoothed
  5916. */
  5917. /**
  5918. * An array determining what time intervals the data is allowed to be
  5919. * grouped to. Each array item is an array where the first value is
  5920. * the time unit and the second value another array of allowed multiples.
  5921. *
  5922. * Defaults to:
  5923. * ```js
  5924. * units: [[
  5925. * 'millisecond', // unit name
  5926. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5927. * ], [
  5928. * 'second',
  5929. * [1, 2, 5, 10, 15, 30]
  5930. * ], [
  5931. * 'minute',
  5932. * [1, 2, 5, 10, 15, 30]
  5933. * ], [
  5934. * 'hour',
  5935. * [1, 2, 3, 4, 6, 8, 12]
  5936. * ], [
  5937. * 'day',
  5938. * [1]
  5939. * ], [
  5940. * 'week',
  5941. * [1]
  5942. * ], [
  5943. * 'month',
  5944. * [1, 3, 6]
  5945. * ], [
  5946. * 'year',
  5947. * null
  5948. * ]]
  5949. * ```
  5950. *
  5951. * @type {Array<Array<string,(Array<number>|null)>>}
  5952. * @apioption plotOptions.series.dataGrouping.units
  5953. */
  5954. /**
  5955. * The approximate pixel width of each group. If for example a series
  5956. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5957. * is performed. If however the series contains so many points that
  5958. * the spacing is less than the groupPixelWidth, Highcharts will try
  5959. * to group it into appropriate groups so that each is more or less
  5960. * two pixels wide. Defaults to `10`.
  5961. *
  5962. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  5963. * Two series with the same data density but different groupPixelWidth
  5964. *
  5965. * @type {number}
  5966. * @default 10
  5967. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  5968. */
  5969. ''; // required by JSDoc parsing
  5970. return dataGrouping;
  5971. });
  5972. _registerModule(_modules, 'Series/OHLC/OHLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  5973. /* *
  5974. *
  5975. * (c) 2010-2021 Torstein Honsi
  5976. *
  5977. * License: www.highcharts.com/license
  5978. *
  5979. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5980. *
  5981. * */
  5982. var __extends = (this && this.__extends) || (function () {
  5983. var extendStatics = function (d,
  5984. b) {
  5985. extendStatics = Object.setPrototypeOf ||
  5986. ({ __proto__: [] } instanceof Array && function (d,
  5987. b) { d.__proto__ = b; }) ||
  5988. function (d,
  5989. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5990. return extendStatics(d, b);
  5991. };
  5992. return function (d, b) {
  5993. extendStatics(d, b);
  5994. function __() { this.constructor = d; }
  5995. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5996. };
  5997. })();
  5998. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  5999. /* *
  6000. *
  6001. * Class
  6002. *
  6003. * */
  6004. var OHLCPoint = /** @class */ (function (_super) {
  6005. __extends(OHLCPoint, _super);
  6006. function OHLCPoint() {
  6007. /* *
  6008. *
  6009. * Properties
  6010. *
  6011. * */
  6012. var _this = _super !== null && _super.apply(this,
  6013. arguments) || this;
  6014. _this.close = void 0;
  6015. _this.high = void 0;
  6016. _this.low = void 0;
  6017. _this.open = void 0;
  6018. _this.options = void 0;
  6019. _this.plotClose = void 0;
  6020. _this.plotOpen = void 0;
  6021. _this.series = void 0;
  6022. return _this;
  6023. /* eslint-enable valid-jsdoc */
  6024. }
  6025. /* *
  6026. *
  6027. * Functions
  6028. *
  6029. * */
  6030. /* eslint-disable valid-jsdoc */
  6031. /**
  6032. * Extend the parent method by adding up or down to the class name.
  6033. * @private
  6034. * @function Highcharts.seriesTypes.ohlc#getClassName
  6035. * @return {string}
  6036. */
  6037. OHLCPoint.prototype.getClassName = function () {
  6038. return _super.prototype.getClassName.call(this) +
  6039. (this.open < this.close ?
  6040. ' highcharts-point-up' :
  6041. ' highcharts-point-down');
  6042. };
  6043. /**
  6044. * Save upColor as point color (#14826).
  6045. * @private
  6046. * @function Highcharts.seriesTypes.ohlc#resolveUpColor
  6047. */
  6048. OHLCPoint.prototype.resolveUpColor = function () {
  6049. if (this.open < this.close &&
  6050. !this.options.color &&
  6051. this.series.options.upColor) {
  6052. this.color = this.series.options.upColor;
  6053. }
  6054. };
  6055. /**
  6056. * Extend the parent method by saving upColor.
  6057. * @private
  6058. * @function Highcharts.seriesTypes.ohlc#resolveColor
  6059. */
  6060. OHLCPoint.prototype.resolveColor = function () {
  6061. _super.prototype.resolveColor.call(this);
  6062. this.resolveUpColor();
  6063. };
  6064. /**
  6065. * Extend the parent method by saving upColor.
  6066. * @private
  6067. * @function Highcharts.seriesTypes.ohlc#getZone
  6068. *
  6069. * @return {Highcharts.SeriesZonesOptionsObject}
  6070. * The zone item.
  6071. */
  6072. OHLCPoint.prototype.getZone = function () {
  6073. var zone = _super.prototype.getZone.call(this);
  6074. this.resolveUpColor();
  6075. return zone;
  6076. };
  6077. return OHLCPoint;
  6078. }(ColumnSeries.prototype.pointClass));
  6079. /* *
  6080. *
  6081. * Default Export
  6082. *
  6083. * */
  6084. return OHLCPoint;
  6085. });
  6086. _registerModule(_modules, 'Series/OHLC/OHLCSeries.js', [_modules['Series/OHLC/OHLCPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (OHLCPoint, SeriesRegistry, U) {
  6087. /* *
  6088. *
  6089. * (c) 2010-2021 Torstein Honsi
  6090. *
  6091. * License: www.highcharts.com/license
  6092. *
  6093. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6094. *
  6095. * */
  6096. var __extends = (this && this.__extends) || (function () {
  6097. var extendStatics = function (d,
  6098. b) {
  6099. extendStatics = Object.setPrototypeOf ||
  6100. ({ __proto__: [] } instanceof Array && function (d,
  6101. b) { d.__proto__ = b; }) ||
  6102. function (d,
  6103. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6104. return extendStatics(d, b);
  6105. };
  6106. return function (d, b) {
  6107. extendStatics(d, b);
  6108. function __() { this.constructor = d; }
  6109. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6110. };
  6111. })();
  6112. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6113. var extend = U.extend,
  6114. merge = U.merge;
  6115. /* *
  6116. *
  6117. * Class
  6118. *
  6119. * */
  6120. /**
  6121. * The ohlc series type.
  6122. *
  6123. * @private
  6124. * @class
  6125. * @name Highcharts.seriesTypes.ohlc
  6126. *
  6127. * @augments Highcharts.Series
  6128. */
  6129. var OHLCSeries = /** @class */ (function (_super) {
  6130. __extends(OHLCSeries, _super);
  6131. function OHLCSeries() {
  6132. /* *
  6133. *
  6134. * Static Properties
  6135. *
  6136. * */
  6137. var _this = _super !== null && _super.apply(this,
  6138. arguments) || this;
  6139. /* *
  6140. *
  6141. * Properties
  6142. *
  6143. * */
  6144. _this.data = void 0;
  6145. _this.options = void 0;
  6146. _this.points = void 0;
  6147. _this.yData = void 0;
  6148. return _this;
  6149. /* eslint-enable valid-jsdoc */
  6150. }
  6151. /* *
  6152. *
  6153. * Functions
  6154. *
  6155. * */
  6156. /* eslint-disable valid-jsdoc */
  6157. /**
  6158. * Draw the data points
  6159. * @private
  6160. */
  6161. OHLCSeries.prototype.drawPoints = function () {
  6162. var series = this,
  6163. points = series.points,
  6164. chart = series.chart,
  6165. /**
  6166. * Extend vertical stem to open and close values.
  6167. */
  6168. extendStem = function (path,
  6169. halfStrokeWidth,
  6170. openOrClose) {
  6171. var start = path[0];
  6172. var end = path[1];
  6173. // We don't need to worry about crisp - openOrClose value
  6174. // is already crisped and halfStrokeWidth should remove it.
  6175. if (typeof start[2] === 'number') {
  6176. start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
  6177. }
  6178. if (typeof end[2] === 'number') {
  6179. end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
  6180. }
  6181. };
  6182. points.forEach(function (point) {
  6183. var plotOpen,
  6184. plotClose,
  6185. crispCorr,
  6186. halfWidth,
  6187. path,
  6188. graphic = point.graphic,
  6189. crispX,
  6190. isNew = !graphic,
  6191. strokeWidth;
  6192. if (typeof point.plotY !== 'undefined') {
  6193. // Create and/or update the graphic
  6194. if (!graphic) {
  6195. point.graphic = graphic = chart.renderer.path()
  6196. .add(series.group);
  6197. }
  6198. if (!chart.styledMode) {
  6199. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  6200. }
  6201. // crisp vector coordinates
  6202. strokeWidth = graphic.strokeWidth();
  6203. crispCorr = (strokeWidth % 2) / 2;
  6204. // #2596:
  6205. crispX = Math.round(point.plotX) - crispCorr;
  6206. halfWidth = Math.round(point.shapeArgs.width / 2);
  6207. // the vertical stem
  6208. path = [
  6209. ['M', crispX, Math.round(point.yBottom)],
  6210. ['L', crispX, Math.round(point.plotHigh)]
  6211. ];
  6212. // open
  6213. if (point.open !== null) {
  6214. plotOpen = Math.round(point.plotOpen) + crispCorr;
  6215. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  6216. extendStem(path, strokeWidth / 2, plotOpen);
  6217. }
  6218. // close
  6219. if (point.close !== null) {
  6220. plotClose = Math.round(point.plotClose) + crispCorr;
  6221. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  6222. extendStem(path, strokeWidth / 2, plotClose);
  6223. }
  6224. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6225. .addClass(point.getClassName(), true);
  6226. }
  6227. });
  6228. };
  6229. /**
  6230. * @private
  6231. * @function Highcarts.seriesTypes.ohlc#init
  6232. * @return {void}
  6233. */
  6234. OHLCSeries.prototype.init = function () {
  6235. _super.prototype.init.apply(this, arguments);
  6236. this.options.stacking = void 0; // #8817
  6237. };
  6238. /**
  6239. * Postprocess mapping between options and SVG attributes
  6240. * @private
  6241. */
  6242. OHLCSeries.prototype.pointAttribs = function (point, state) {
  6243. var attribs = _super.prototype.pointAttribs.call(this,
  6244. point,
  6245. state),
  6246. options = this.options;
  6247. delete attribs.fill;
  6248. if (!point.options.color &&
  6249. options.upColor &&
  6250. point.open < point.close) {
  6251. attribs.stroke = options.upColor;
  6252. }
  6253. return attribs;
  6254. };
  6255. OHLCSeries.prototype.toYData = function (point) {
  6256. // return a plain array for speedy calculation
  6257. return [point.open, point.high, point.low, point.close];
  6258. };
  6259. /**
  6260. * Translate data points from raw values x and y to plotX and plotY
  6261. *
  6262. * @private
  6263. * @function Highcharts.seriesTypes.ohlc#translate
  6264. * @return {void}
  6265. */
  6266. OHLCSeries.prototype.translate = function () {
  6267. var series = this,
  6268. yAxis = series.yAxis,
  6269. hasModifyValue = !!series.modifyValue,
  6270. translated = [
  6271. 'plotOpen',
  6272. 'plotHigh',
  6273. 'plotLow',
  6274. 'plotClose',
  6275. 'yBottom'
  6276. ]; // translate OHLC for
  6277. _super.prototype.translate.apply(series);
  6278. // Do the translation
  6279. series.points.forEach(function (point) {
  6280. [point.open, point.high, point.low, point.close, point.low]
  6281. .forEach(function (value, i) {
  6282. if (value !== null) {
  6283. if (hasModifyValue) {
  6284. value = series.modifyValue(value);
  6285. }
  6286. point[translated[i]] =
  6287. yAxis.toPixels(value, true);
  6288. }
  6289. });
  6290. // Align the tooltip to the high value to avoid covering the
  6291. // point
  6292. point.tooltipPos[1] =
  6293. point.plotHigh + yAxis.pos - series.chart.plotTop;
  6294. });
  6295. };
  6296. /**
  6297. * An OHLC chart is a style of financial chart used to describe price
  6298. * movements over time. It displays open, high, low and close values per
  6299. * data point.
  6300. *
  6301. * @sample stock/demo/ohlc/
  6302. * OHLC chart
  6303. *
  6304. * @extends plotOptions.column
  6305. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  6306. * stack
  6307. * @product highstock
  6308. * @optionparent plotOptions.ohlc
  6309. */
  6310. OHLCSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  6311. /**
  6312. * The approximate pixel width of each group. If for example a series
  6313. * with 30 points is displayed over a 600 pixel wide plot area, no
  6314. * grouping is performed. If however the series contains so many points
  6315. * that the spacing is less than the groupPixelWidth, Highcharts will
  6316. * try to group it into appropriate groups so that each is more or less
  6317. * two pixels wide. Defaults to `5`.
  6318. *
  6319. * @type {number}
  6320. * @default 5
  6321. * @product highstock
  6322. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  6323. */
  6324. /**
  6325. * The pixel width of the line/border. Defaults to `1`.
  6326. *
  6327. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  6328. * A greater line width
  6329. *
  6330. * @type {number}
  6331. * @default 1
  6332. * @product highstock
  6333. *
  6334. * @private
  6335. */
  6336. lineWidth: 1,
  6337. tooltip: {
  6338. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  6339. '<b> {series.name}</b><br/>' +
  6340. 'Open: {point.open}<br/>' +
  6341. 'High: {point.high}<br/>' +
  6342. 'Low: {point.low}<br/>' +
  6343. 'Close: {point.close}<br/>'
  6344. },
  6345. threshold: null,
  6346. states: {
  6347. /**
  6348. * @extends plotOptions.column.states.hover
  6349. * @product highstock
  6350. */
  6351. hover: {
  6352. /**
  6353. * The pixel width of the line representing the OHLC point.
  6354. *
  6355. * @type {number}
  6356. * @default 3
  6357. * @product highstock
  6358. */
  6359. lineWidth: 3
  6360. }
  6361. },
  6362. /**
  6363. * Determines which one of `open`, `high`, `low`, `close` values should
  6364. * be represented as `point.y`, which is later used to set dataLabel
  6365. * position and [compare](#plotOptions.series.compare).
  6366. *
  6367. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  6368. * Possible values
  6369. *
  6370. * @type {string}
  6371. * @default close
  6372. * @validvalue ["open", "high", "low", "close"]
  6373. * @product highstock
  6374. * @apioption plotOptions.ohlc.pointValKey
  6375. */
  6376. /**
  6377. * @default close
  6378. * @apioption plotOptions.ohlc.colorKey
  6379. */
  6380. /**
  6381. * Line color for up points.
  6382. *
  6383. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6384. * @product highstock
  6385. * @apioption plotOptions.ohlc.upColor
  6386. */
  6387. stickyTracking: true
  6388. });
  6389. return OHLCSeries;
  6390. }(ColumnSeries));
  6391. extend(OHLCSeries.prototype, {
  6392. animate: null,
  6393. directTouch: false,
  6394. pointArrayMap: ['open', 'high', 'low', 'close'],
  6395. pointAttrToOptions: {
  6396. stroke: 'color',
  6397. 'stroke-width': 'lineWidth'
  6398. },
  6399. pointValKey: 'close'
  6400. });
  6401. OHLCSeries.prototype.pointClass = OHLCPoint;
  6402. SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
  6403. /* *
  6404. *
  6405. * Default Export
  6406. *
  6407. * */
  6408. /* *
  6409. *
  6410. * API Options
  6411. *
  6412. * */
  6413. /**
  6414. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  6415. * specified, it is inherited from [chart.type](#chart.type).
  6416. *
  6417. * @extends series,plotOptions.ohlc
  6418. * @excluding dataParser, dataURL
  6419. * @product highstock
  6420. * @apioption series.ohlc
  6421. */
  6422. /**
  6423. * An array of data points for the series. For the `ohlc` series type,
  6424. * points can be given in the following ways:
  6425. *
  6426. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6427. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6428. * as the name of the point, and the `x` value is inferred. The `x` value can
  6429. * also be omitted, in which case the inner arrays should be of length 4\.
  6430. * Then the `x` value is automatically calculated, either starting at 0 and
  6431. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6432. * series options.
  6433. * ```js
  6434. * data: [
  6435. * [0, 6, 5, 6, 7],
  6436. * [1, 9, 4, 8, 2],
  6437. * [2, 6, 3, 4, 10]
  6438. * ]
  6439. * ```
  6440. *
  6441. * 2. An array of objects with named values. The following snippet shows only a
  6442. * few settings, see the complete options set below. If the total number of
  6443. * data points exceeds the series'
  6444. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  6445. * available.
  6446. * ```js
  6447. * data: [{
  6448. * x: 1,
  6449. * open: 3,
  6450. * high: 4,
  6451. * low: 5,
  6452. * close: 2,
  6453. * name: "Point2",
  6454. * color: "#00FF00"
  6455. * }, {
  6456. * x: 1,
  6457. * open: 4,
  6458. * high: 3,
  6459. * low: 6,
  6460. * close: 7,
  6461. * name: "Point1",
  6462. * color: "#FF00FF"
  6463. * }]
  6464. * ```
  6465. *
  6466. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6467. * @extends series.arearange.data
  6468. * @excluding y, marker
  6469. * @product highstock
  6470. * @apioption series.ohlc.data
  6471. */
  6472. /**
  6473. * The closing value of each data point.
  6474. *
  6475. * @type {number}
  6476. * @product highstock
  6477. * @apioption series.ohlc.data.close
  6478. */
  6479. /**
  6480. * The opening value of each data point.
  6481. *
  6482. * @type {number}
  6483. * @product highstock
  6484. * @apioption series.ohlc.data.open
  6485. */
  6486. ''; // adds doclets above to transpilat
  6487. return OHLCSeries;
  6488. });
  6489. _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) {
  6490. /* *
  6491. *
  6492. * (c) 2010-2021 Torstein Honsi
  6493. *
  6494. * License: www.highcharts.com/license
  6495. *
  6496. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6497. *
  6498. * */
  6499. var __extends = (this && this.__extends) || (function () {
  6500. var extendStatics = function (d,
  6501. b) {
  6502. extendStatics = Object.setPrototypeOf ||
  6503. ({ __proto__: [] } instanceof Array && function (d,
  6504. b) { d.__proto__ = b; }) ||
  6505. function (d,
  6506. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6507. return extendStatics(d, b);
  6508. };
  6509. return function (d, b) {
  6510. extendStatics(d, b);
  6511. function __() { this.constructor = d; }
  6512. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6513. };
  6514. })();
  6515. var defaultOptions = O.defaultOptions;
  6516. var _a = SeriesRegistry.seriesTypes,
  6517. ColumnSeries = _a.column,
  6518. OHLCSeries = _a.ohlc;
  6519. var merge = U.merge;
  6520. /* *
  6521. *
  6522. * Code
  6523. *
  6524. * */
  6525. /**
  6526. * The candlestick series type.
  6527. *
  6528. * @private
  6529. * @class
  6530. * @name Highcharts.seriesTypes.candlestick
  6531. *
  6532. * @augments Highcharts.seriesTypes.ohlc
  6533. */
  6534. var CandlestickSeries = /** @class */ (function (_super) {
  6535. __extends(CandlestickSeries, _super);
  6536. function CandlestickSeries() {
  6537. /* *
  6538. *
  6539. * Static properties
  6540. *
  6541. * */
  6542. var _this = _super !== null && _super.apply(this,
  6543. arguments) || this;
  6544. /* *
  6545. *
  6546. * Properties
  6547. *
  6548. * */
  6549. _this.data = void 0;
  6550. _this.options = void 0;
  6551. _this.points = void 0;
  6552. return _this;
  6553. }
  6554. /* *
  6555. *
  6556. * Functions
  6557. *
  6558. * */
  6559. /* eslint-disable valid-jsdoc */
  6560. /**
  6561. * Postprocess mapping between options and SVG attributes
  6562. *
  6563. * @private
  6564. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  6565. */
  6566. CandlestickSeries.prototype.pointAttribs = function (point, state) {
  6567. var attribs = ColumnSeries.prototype.pointAttribs.call(this,
  6568. point,
  6569. state),
  6570. options = this.options,
  6571. isUp = point.open < point.close,
  6572. stroke = options.lineColor || this.color,
  6573. color = point.color || this.color, // (#14826)
  6574. stateOptions;
  6575. attribs['stroke-width'] = options.lineWidth;
  6576. attribs.fill = point.options.color ||
  6577. (isUp ? (options.upColor || color) : color);
  6578. attribs.stroke = point.options.lineColor ||
  6579. (isUp ? (options.upLineColor || stroke) : stroke);
  6580. // Select or hover states
  6581. if (state) {
  6582. stateOptions = options.states[state];
  6583. attribs.fill = stateOptions.color || attribs.fill;
  6584. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  6585. attribs['stroke-width'] =
  6586. stateOptions.lineWidth || attribs['stroke-width'];
  6587. }
  6588. return attribs;
  6589. };
  6590. /**
  6591. * Draw the data points.
  6592. *
  6593. * @private
  6594. * @function Highcharts.seriesTypes.candlestick#drawPoints
  6595. * @return {void}
  6596. */
  6597. CandlestickSeries.prototype.drawPoints = function () {
  6598. var series = this,
  6599. points = series.points,
  6600. chart = series.chart,
  6601. reversedYAxis = series.yAxis.reversed;
  6602. points.forEach(function (point) {
  6603. var graphic = point.graphic,
  6604. plotOpen,
  6605. plotClose,
  6606. topBox,
  6607. bottomBox,
  6608. hasTopWhisker,
  6609. hasBottomWhisker,
  6610. crispCorr,
  6611. crispX,
  6612. path,
  6613. halfWidth,
  6614. isNew = !graphic;
  6615. if (typeof point.plotY !== 'undefined') {
  6616. if (!graphic) {
  6617. point.graphic = graphic = chart.renderer.path()
  6618. .add(series.group);
  6619. }
  6620. if (!series.chart.styledMode) {
  6621. graphic
  6622. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  6623. .shadow(series.options.shadow);
  6624. }
  6625. // Crisp vector coordinates
  6626. crispCorr = (graphic.strokeWidth() % 2) / 2;
  6627. // #2596:
  6628. crispX = Math.round(point.plotX) - crispCorr;
  6629. plotOpen = point.plotOpen;
  6630. plotClose = point.plotClose;
  6631. topBox = Math.min(plotOpen, plotClose);
  6632. bottomBox = Math.max(plotOpen, plotClose);
  6633. halfWidth = Math.round(point.shapeArgs.width / 2);
  6634. hasTopWhisker = reversedYAxis ?
  6635. bottomBox !== point.yBottom :
  6636. Math.round(topBox) !==
  6637. Math.round(point.plotHigh);
  6638. hasBottomWhisker = reversedYAxis ?
  6639. Math.round(topBox) !==
  6640. Math.round(point.plotHigh) :
  6641. bottomBox !== point.yBottom;
  6642. topBox = Math.round(topBox) + crispCorr;
  6643. bottomBox = Math.round(bottomBox) + crispCorr;
  6644. // Create the path. Due to a bug in Chrome 49, the path is
  6645. // first instanciated with no values, then the values
  6646. // pushed. For unknown reasons, instanciating the path array
  6647. // with all the values would lead to a crash when updating
  6648. // frequently (#5193).
  6649. path = [];
  6650. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  6651. ['M', crispX, topBox], [
  6652. 'L',
  6653. // #460, #2094
  6654. crispX,
  6655. hasTopWhisker ?
  6656. Math.round(reversedYAxis ?
  6657. point.yBottom :
  6658. point.plotHigh) :
  6659. topBox
  6660. ], ['M', crispX, bottomBox], [
  6661. 'L',
  6662. // #460, #2094
  6663. crispX,
  6664. hasBottomWhisker ?
  6665. Math.round(reversedYAxis ?
  6666. point.plotHigh :
  6667. point.yBottom) :
  6668. bottomBox
  6669. ]);
  6670. graphic[isNew ? 'attr' : 'animate']({ d: path })
  6671. .addClass(point.getClassName(), true);
  6672. }
  6673. });
  6674. /* eslint-enable valid-jsdoc */
  6675. };
  6676. /**
  6677. * A candlestick chart is a style of financial chart used to describe price
  6678. * movements over time.
  6679. *
  6680. * @sample stock/demo/candlestick/
  6681. * Candlestick chart
  6682. *
  6683. * @extends plotOptions.ohlc
  6684. * @excluding borderColor,borderRadius,borderWidth
  6685. * @product highstock
  6686. * @optionparent plotOptions.candlestick
  6687. */
  6688. CandlestickSeries.defaultOptions = merge(OHLCSeries.defaultOptions, defaultOptions.plotOptions, {
  6689. /**
  6690. * The specific line color for up candle sticks. The default is to
  6691. * inherit the general `lineColor` setting.
  6692. *
  6693. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6694. * Candlestick line colors
  6695. *
  6696. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6697. * @since 1.3.6
  6698. * @product highstock
  6699. * @apioption plotOptions.candlestick.upLineColor
  6700. */
  6701. /**
  6702. * @type {Highcharts.DataGroupingApproximationValue|Function}
  6703. * @default ohlc
  6704. * @product highstock
  6705. * @apioption plotOptions.candlestick.dataGrouping.approximation
  6706. */
  6707. states: {
  6708. /**
  6709. * @extends plotOptions.column.states.hover
  6710. * @product highstock
  6711. */
  6712. hover: {
  6713. /**
  6714. * The pixel width of the line/border around the candlestick.
  6715. *
  6716. * @product highstock
  6717. */
  6718. lineWidth: 2
  6719. }
  6720. },
  6721. /**
  6722. * @extends plotOptions.ohlc.tooltip
  6723. */
  6724. tooltip: defaultOptions.plotOptions.ohlc.tooltip,
  6725. /**
  6726. * @type {number|null}
  6727. * @product highstock
  6728. */
  6729. threshold: null,
  6730. /**
  6731. * The color of the line/border of the candlestick.
  6732. *
  6733. * In styled mode, the line stroke can be set with the
  6734. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6735. *
  6736. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  6737. *
  6738. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  6739. * Candlestick line colors
  6740. *
  6741. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6742. * @default #000000
  6743. * @product highstock
  6744. */
  6745. lineColor: palette.neutralColor100,
  6746. /**
  6747. * The pixel width of the candlestick line/border. Defaults to `1`.
  6748. *
  6749. *
  6750. * In styled mode, the line stroke width can be set with the
  6751. * `.highcharts-candlestick-series .highcahrts-point` rule.
  6752. *
  6753. * @product highstock
  6754. */
  6755. lineWidth: 1,
  6756. /**
  6757. * The fill color of the candlestick when values are rising.
  6758. *
  6759. * In styled mode, the up color can be set with the
  6760. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  6761. *
  6762. * @sample {highstock} stock/plotoptions/candlestick-color/
  6763. * Custom colors
  6764. * @sample {highstock} highcharts/css/candlestick/
  6765. * Colors in styled mode
  6766. *
  6767. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6768. * @default #ffffff
  6769. * @product highstock
  6770. */
  6771. upColor: palette.backgroundColor,
  6772. /**
  6773. * @product highstock
  6774. */
  6775. stickyTracking: true
  6776. });
  6777. return CandlestickSeries;
  6778. }(OHLCSeries));
  6779. SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
  6780. /* *
  6781. *
  6782. * Default Export
  6783. *
  6784. * */
  6785. /* *
  6786. *
  6787. * API Options
  6788. *
  6789. * */
  6790. /**
  6791. * A `candlestick` series. If the [type](#series.candlestick.type)
  6792. * option is not specified, it is inherited from [chart.type](
  6793. * #chart.type).
  6794. *
  6795. * @type {*}
  6796. * @extends series,plotOptions.candlestick
  6797. * @excluding dataParser, dataURL, marker
  6798. * @product highstock
  6799. * @apioption series.candlestick
  6800. */
  6801. /**
  6802. * An array of data points for the series. For the `candlestick` series
  6803. * type, points can be given in the following ways:
  6804. *
  6805. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  6806. * to `x,open,high,low,close`. If the first value is a string, it is applied
  6807. * as the name of the point, and the `x` value is inferred. The `x` value can
  6808. * also be omitted, in which case the inner arrays should be of length 4.
  6809. * Then the `x` value is automatically calculated, either starting at 0 and
  6810. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  6811. * series options.
  6812. * ```js
  6813. * data: [
  6814. * [0, 7, 2, 0, 4],
  6815. * [1, 1, 4, 2, 8],
  6816. * [2, 3, 3, 9, 3]
  6817. * ]
  6818. * ```
  6819. *
  6820. * 2. An array of objects with named values. The following snippet shows only a
  6821. * few settings, see the complete options set below. If the total number of
  6822. * data points exceeds the series'
  6823. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  6824. * available.
  6825. * ```js
  6826. * data: [{
  6827. * x: 1,
  6828. * open: 9,
  6829. * high: 2,
  6830. * low: 4,
  6831. * close: 6,
  6832. * name: "Point2",
  6833. * color: "#00FF00"
  6834. * }, {
  6835. * x: 1,
  6836. * open: 1,
  6837. * high: 4,
  6838. * low: 7,
  6839. * close: 7,
  6840. * name: "Point1",
  6841. * color: "#FF00FF"
  6842. * }]
  6843. * ```
  6844. *
  6845. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  6846. * @extends series.ohlc.data
  6847. * @excluding y
  6848. * @product highstock
  6849. * @apioption series.candlestick.data
  6850. */
  6851. ''; // adds doclets above to transpilat
  6852. return CandlestickSeries;
  6853. });
  6854. _registerModule(_modules, 'Series/Flags/FlagsPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  6855. /* *
  6856. *
  6857. * (c) 2010-2021 Torstein Honsi
  6858. *
  6859. * License: www.highcharts.com/license
  6860. *
  6861. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6862. *
  6863. * */
  6864. var __extends = (this && this.__extends) || (function () {
  6865. var extendStatics = function (d,
  6866. b) {
  6867. extendStatics = Object.setPrototypeOf ||
  6868. ({ __proto__: [] } instanceof Array && function (d,
  6869. b) { d.__proto__ = b; }) ||
  6870. function (d,
  6871. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6872. return extendStatics(d, b);
  6873. };
  6874. return function (d, b) {
  6875. extendStatics(d, b);
  6876. function __() { this.constructor = d; }
  6877. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6878. };
  6879. })();
  6880. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  6881. var isNumber = U.isNumber;
  6882. /* *
  6883. *
  6884. * Class
  6885. *
  6886. * */
  6887. var FlagsPoint = /** @class */ (function (_super) {
  6888. __extends(FlagsPoint, _super);
  6889. function FlagsPoint() {
  6890. /* *
  6891. *
  6892. * Properties
  6893. *
  6894. * */
  6895. var _this = _super !== null && _super.apply(this,
  6896. arguments) || this;
  6897. _this.options = void 0;
  6898. _this.series = void 0;
  6899. return _this;
  6900. }
  6901. /* *
  6902. *
  6903. * Functions
  6904. *
  6905. * */
  6906. /* eslint-disable valid-jsdoc */
  6907. /**
  6908. * @private
  6909. */
  6910. FlagsPoint.prototype.isValid = function () {
  6911. // #9233 - Prevent from treating flags as null points (even if
  6912. // they have no y values defined).
  6913. return isNumber(this.y) || typeof this.y === 'undefined';
  6914. };
  6915. /**
  6916. * @private
  6917. */
  6918. FlagsPoint.prototype.hasNewShapeType = function () {
  6919. var shape = this.options.shape || this.series.options.shape;
  6920. return this.graphic && shape && shape !== this.graphic.symbolKey;
  6921. };
  6922. return FlagsPoint;
  6923. }(ColumnSeries.prototype.pointClass));
  6924. /* *
  6925. *
  6926. * Default Export
  6927. *
  6928. * */
  6929. return FlagsPoint;
  6930. });
  6931. _registerModule(_modules, 'Mixins/OnSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (ColumnSeries, Series, U) {
  6932. /* *
  6933. *
  6934. * (c) 2010-2021 Torstein Honsi
  6935. *
  6936. * License: www.highcharts.com/license
  6937. *
  6938. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6939. *
  6940. * */
  6941. var columnProto = ColumnSeries.prototype;
  6942. var seriesProto = Series.prototype;
  6943. var defined = U.defined,
  6944. stableSort = U.stableSort;
  6945. /**
  6946. * @private
  6947. * @mixin onSeriesMixin
  6948. */
  6949. var onSeriesMixin = {
  6950. /* eslint-disable valid-jsdoc */
  6951. /**
  6952. * Override getPlotBox. If the onSeries option is valid,
  6953. return the plot box
  6954. * of the onSeries,
  6955. otherwise proceed as usual.
  6956. *
  6957. * @private
  6958. * @function onSeriesMixin.getPlotBox
  6959. * @return {Highcharts.SeriesPlotBoxObject}
  6960. */
  6961. getPlotBox: function () {
  6962. return seriesProto.getPlotBox.call((this.options.onSeries &&
  6963. this.chart.get(this.options.onSeries)) || this);
  6964. },
  6965. /**
  6966. * Extend the translate method by placing the point on the related series
  6967. *
  6968. * @private
  6969. * @function onSeriesMixin.translate
  6970. * @return {void}
  6971. */
  6972. translate: function () {
  6973. columnProto.translate.apply(this);
  6974. var series = this,
  6975. options = series.options,
  6976. chart = series.chart,
  6977. points = series.points,
  6978. cursor = points.length - 1,
  6979. point,
  6980. lastPoint,
  6981. optionsOnSeries = options.onSeries,
  6982. onSeries = (optionsOnSeries &&
  6983. chart.get(optionsOnSeries)),
  6984. onKey = options.onKey || 'y',
  6985. step = onSeries && onSeries.options.step,
  6986. onData = (onSeries && onSeries.points),
  6987. i = onData && onData.length,
  6988. inverted = chart.inverted,
  6989. xAxis = series.xAxis,
  6990. yAxis = series.yAxis,
  6991. xOffset = 0,
  6992. leftPoint,
  6993. lastX,
  6994. rightPoint,
  6995. currentDataGrouping,
  6996. distanceRatio;
  6997. // relate to a master series
  6998. if (onSeries && onSeries.visible && i) {
  6999. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  7000. currentDataGrouping = onSeries.currentDataGrouping;
  7001. lastX = (onData[i - 1].x +
  7002. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  7003. // sort the data points
  7004. stableSort(points, function (a, b) {
  7005. return (a.x - b.x);
  7006. });
  7007. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  7008. while (i-- && points[cursor]) {
  7009. leftPoint = onData[i];
  7010. point = points[cursor];
  7011. point.y = leftPoint.y;
  7012. if (leftPoint.x <= point.x &&
  7013. typeof leftPoint[onKey] !== 'undefined') {
  7014. if (point.x <= lastX) { // #803
  7015. point.plotY = leftPoint[onKey];
  7016. // interpolate between points, #666
  7017. if (leftPoint.x < point.x &&
  7018. !step) {
  7019. rightPoint = onData[i + 1];
  7020. if (rightPoint &&
  7021. typeof rightPoint[onKey] !== 'undefined') {
  7022. // the distance ratio, between 0 and 1
  7023. distanceRatio =
  7024. (point.x - leftPoint.x) /
  7025. (rightPoint.x - leftPoint.x);
  7026. point.plotY +=
  7027. distanceRatio *
  7028. // the plotY distance
  7029. (rightPoint[onKey] - leftPoint[onKey]);
  7030. point.y +=
  7031. distanceRatio *
  7032. (rightPoint.y - leftPoint.y);
  7033. }
  7034. }
  7035. }
  7036. cursor--;
  7037. i++; // check again for points in the same x position
  7038. if (cursor < 0) {
  7039. break;
  7040. }
  7041. }
  7042. }
  7043. }
  7044. // Add plotY position and handle stacking
  7045. points.forEach(function (point, i) {
  7046. var stackIndex;
  7047. point.plotX += xOffset; // #2049
  7048. // Undefined plotY means the point is either on axis, outside series
  7049. // range or hidden series. If the series is outside the range of the
  7050. // x axis it should fall through with an undefined plotY, but then
  7051. // we must remove the shapeArgs (#847). For inverted charts, we need
  7052. // to calculate position anyway, because series.invertGroups is not
  7053. // defined
  7054. if (typeof point.plotY === 'undefined' || inverted) {
  7055. if (point.plotX >= 0 &&
  7056. point.plotX <= xAxis.len) {
  7057. // We're inside xAxis range
  7058. if (inverted) {
  7059. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  7060. point.plotX = defined(point.y) ?
  7061. yAxis.translate(point.y, 0, 0, 0, 1) :
  7062. 0;
  7063. }
  7064. else {
  7065. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  7066. xAxis.offset; // For the windbarb demo
  7067. }
  7068. }
  7069. else {
  7070. point.shapeArgs = {}; // 847
  7071. }
  7072. }
  7073. // if multiple flags appear at the same x, order them into a stack
  7074. lastPoint = points[i - 1];
  7075. if (lastPoint && lastPoint.plotX === point.plotX) {
  7076. if (typeof lastPoint.stackIndex === 'undefined') {
  7077. lastPoint.stackIndex = 0;
  7078. }
  7079. stackIndex = lastPoint.stackIndex + 1;
  7080. }
  7081. point.stackIndex = stackIndex; // #3639
  7082. });
  7083. this.onSeries = onSeries;
  7084. }
  7085. /* eslint-enable valid-jsdoc */
  7086. };
  7087. return onSeriesMixin;
  7088. });
  7089. _registerModule(_modules, 'Series/Flags/FlagsSymbols.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGRenderer.js']], function (H, SVGRenderer) {
  7090. /* *
  7091. *
  7092. * Imports
  7093. *
  7094. * */
  7095. var Renderer = H.Renderer,
  7096. VMLRenderer = H.VMLRenderer;
  7097. var symbols = SVGRenderer.prototype.symbols;
  7098. /* *
  7099. *
  7100. * Symbols
  7101. *
  7102. * */
  7103. // create the flag icon with anchor
  7104. symbols.flag = function (x, y, w, h, options) {
  7105. var anchorX = (options && options.anchorX) || x,
  7106. anchorY = (options && options.anchorY) || y;
  7107. // To do: unwanted any cast because symbols.circle has wrong type, it
  7108. // actually returns an SVGPathArray
  7109. var path = symbols.circle(anchorX - 1,
  7110. anchorY - 1, 2, 2);
  7111. 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']);
  7112. return path;
  7113. };
  7114. /**
  7115. * Create the circlepin and squarepin icons with anchor.
  7116. * @private
  7117. * @param {string} shape - circle or square
  7118. * @return {void}
  7119. */
  7120. function createPinSymbol(shape) {
  7121. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  7122. var anchorX = options && options.anchorX,
  7123. anchorY = options && options.anchorY,
  7124. path;
  7125. // For single-letter flags, make sure circular flags are not taller
  7126. // than their width
  7127. if (shape === 'circle' && h > w) {
  7128. x -= Math.round((h - w) / 2);
  7129. w = h;
  7130. }
  7131. path = (symbols[shape])(x, y, w, h);
  7132. if (anchorX && anchorY) {
  7133. /**
  7134. * If the label is below the anchor, draw the connecting line from
  7135. * the top edge of the label, otherwise start drawing from the
  7136. * bottom edge
  7137. */
  7138. var labelX = anchorX;
  7139. if (shape === 'circle') {
  7140. labelX = x + w / 2;
  7141. }
  7142. else {
  7143. var startSeg = path[0];
  7144. var endSeg = path[1];
  7145. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  7146. labelX = (startSeg[1] + endSeg[1]) / 2;
  7147. }
  7148. }
  7149. var labelY = (y > anchorY) ? y : y + h;
  7150. path.push([
  7151. 'M',
  7152. labelX,
  7153. labelY
  7154. ], [
  7155. 'L',
  7156. anchorX,
  7157. anchorY
  7158. ]);
  7159. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  7160. }
  7161. return path;
  7162. };
  7163. }
  7164. createPinSymbol('circle');
  7165. createPinSymbol('square');
  7166. /**
  7167. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  7168. * Even VML browsers need this in order to generate shapes in export. Now share
  7169. * them with the VMLRenderer.
  7170. */
  7171. if (Renderer === VMLRenderer) {
  7172. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  7173. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  7174. });
  7175. }
  7176. /* *
  7177. *
  7178. * Default Export
  7179. *
  7180. * */
  7181. return symbols;
  7182. });
  7183. _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) {
  7184. /* *
  7185. *
  7186. * (c) 2010-2021 Torstein Honsi
  7187. *
  7188. * License: www.highcharts.com/license
  7189. *
  7190. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7191. *
  7192. * */
  7193. var __extends = (this && this.__extends) || (function () {
  7194. var extendStatics = function (d,
  7195. b) {
  7196. extendStatics = Object.setPrototypeOf ||
  7197. ({ __proto__: [] } instanceof Array && function (d,
  7198. b) { d.__proto__ = b; }) ||
  7199. function (d,
  7200. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7201. return extendStatics(d, b);
  7202. };
  7203. return function (d, b) {
  7204. extendStatics(d, b);
  7205. function __() { this.constructor = d; }
  7206. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  7207. };
  7208. })();
  7209. var noop = H.noop;
  7210. var Series = SeriesRegistry.series,
  7211. ColumnSeries = SeriesRegistry.seriesTypes.column;
  7212. var addEvent = U.addEvent,
  7213. defined = U.defined,
  7214. extend = U.extend,
  7215. merge = U.merge,
  7216. objectEach = U.objectEach,
  7217. wrap = U.wrap;
  7218. /**
  7219. * The Flags series.
  7220. *
  7221. * @private
  7222. * @class
  7223. * @name Highcharts.seriesTypes.flags
  7224. *
  7225. * @augments Highcharts.Series
  7226. */
  7227. var FlagsSeries = /** @class */ (function (_super) {
  7228. __extends(FlagsSeries, _super);
  7229. function FlagsSeries() {
  7230. /* *
  7231. *
  7232. * Static Properties
  7233. *
  7234. * */
  7235. var _this = _super !== null && _super.apply(this,
  7236. arguments) || this;
  7237. /* *
  7238. *
  7239. * Properties
  7240. *
  7241. * */
  7242. _this.data = void 0;
  7243. _this.options = void 0;
  7244. _this.points = void 0;
  7245. return _this;
  7246. /* eslint-enable valid-jsdoc */
  7247. }
  7248. /* *
  7249. *
  7250. * Functions
  7251. *
  7252. * */
  7253. /* eslint-disable valid-jsdoc */
  7254. /**
  7255. * Disable animation, but keep clipping (#8546).
  7256. * @private
  7257. */
  7258. FlagsSeries.prototype.animate = function (init) {
  7259. if (init) {
  7260. this.setClip();
  7261. }
  7262. };
  7263. /**
  7264. * Draw the markers.
  7265. * @private
  7266. */
  7267. FlagsSeries.prototype.drawPoints = function () {
  7268. var series = this,
  7269. points = series.points,
  7270. chart = series.chart,
  7271. renderer = chart.renderer,
  7272. plotX,
  7273. plotY,
  7274. inverted = chart.inverted,
  7275. options = series.options,
  7276. optionsY = options.y,
  7277. shape,
  7278. i,
  7279. point,
  7280. graphic,
  7281. stackIndex,
  7282. anchorY,
  7283. attribs,
  7284. outsideRight,
  7285. yAxis = series.yAxis,
  7286. boxesMap = {},
  7287. boxes = [],
  7288. centered;
  7289. i = points.length;
  7290. while (i--) {
  7291. point = points[i];
  7292. outsideRight =
  7293. (inverted ? point.plotY : point.plotX) >
  7294. series.xAxis.len;
  7295. plotX = point.plotX;
  7296. stackIndex = point.stackIndex;
  7297. shape = point.options.shape || options.shape;
  7298. plotY = point.plotY;
  7299. if (typeof plotY !== 'undefined') {
  7300. plotY = point.plotY + optionsY -
  7301. (typeof stackIndex !== 'undefined' &&
  7302. (stackIndex * options.stackDistance));
  7303. }
  7304. // skip connectors for higher level stacked points
  7305. point.anchorX = stackIndex ? void 0 : point.plotX;
  7306. anchorY = stackIndex ? void 0 : point.plotY;
  7307. centered = shape !== 'flag';
  7308. graphic = point.graphic;
  7309. // Only draw the point if y is defined and the flag is within
  7310. // the visible area
  7311. if (typeof plotY !== 'undefined' &&
  7312. plotX >= 0 &&
  7313. !outsideRight) {
  7314. // #15384
  7315. if (graphic && point.hasNewShapeType()) {
  7316. graphic = graphic.destroy();
  7317. }
  7318. // Create the flag
  7319. if (!graphic) {
  7320. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML)
  7321. .addClass('highcharts-point')
  7322. .add(series.markerGroup);
  7323. // Add reference to the point for tracker (#6303)
  7324. if (point.graphic.div) {
  7325. point.graphic.div.point = point;
  7326. }
  7327. graphic.isNew = true;
  7328. }
  7329. graphic.attr({
  7330. align: centered ? 'center' : 'left',
  7331. width: options.width,
  7332. height: options.height,
  7333. 'text-align': options.textAlign
  7334. });
  7335. if (!chart.styledMode) {
  7336. graphic
  7337. .attr(series.pointAttribs(point))
  7338. .css(merge(options.style, point.style))
  7339. .shadow(options.shadow);
  7340. }
  7341. if (plotX > 0) { // #3119
  7342. plotX -= graphic.strokeWidth() % 2; // #4285
  7343. }
  7344. // Plant the flag
  7345. attribs = {
  7346. y: plotY,
  7347. anchorY: anchorY
  7348. };
  7349. if (options.allowOverlapX) {
  7350. attribs.x = plotX;
  7351. attribs.anchorX = point.anchorX;
  7352. }
  7353. graphic.attr({
  7354. text: point.options.title || options.title || 'A'
  7355. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  7356. // Rig for the distribute function
  7357. if (!options.allowOverlapX) {
  7358. if (!boxesMap[point.plotX]) {
  7359. boxesMap[point.plotX] = {
  7360. align: centered ? 0.5 : 0,
  7361. size: graphic.width,
  7362. target: plotX,
  7363. anchorX: plotX
  7364. };
  7365. }
  7366. else {
  7367. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  7368. }
  7369. }
  7370. // Set the tooltip anchor position
  7371. point.tooltipPos = [
  7372. plotX,
  7373. plotY + yAxis.pos - chart.plotTop
  7374. ]; // #6327
  7375. }
  7376. else if (graphic) {
  7377. point.graphic = graphic.destroy();
  7378. }
  7379. }
  7380. // Handle X-dimension overlapping
  7381. if (!options.allowOverlapX) {
  7382. objectEach(boxesMap, function (box) {
  7383. box.plotX = box.anchorX;
  7384. boxes.push(box);
  7385. });
  7386. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  7387. points.forEach(function (point) {
  7388. var box = point.graphic && boxesMap[point.plotX];
  7389. if (box) {
  7390. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  7391. x: box.pos + box.align * box.size,
  7392. anchorX: point.anchorX
  7393. });
  7394. // Hide flag when its box position is not specified
  7395. // (#8573, #9299)
  7396. if (!defined(box.pos)) {
  7397. point.graphic.attr({
  7398. x: -9999,
  7399. anchorX: -9999
  7400. });
  7401. point.graphic.isNew = true;
  7402. }
  7403. else {
  7404. point.graphic.isNew = false;
  7405. }
  7406. }
  7407. });
  7408. }
  7409. // Can be a mix of SVG and HTML and we need events for both (#6303)
  7410. if (options.useHTML) {
  7411. wrap(series.markerGroup, 'on', function (proceed) {
  7412. return SVGElement.prototype.on.apply(
  7413. // for HTML
  7414. // eslint-disable-next-line no-invalid-this
  7415. proceed.apply(this, [].slice.call(arguments, 1)),
  7416. // and for SVG
  7417. [].slice.call(arguments, 1));
  7418. });
  7419. }
  7420. };
  7421. /**
  7422. * Extend the column trackers with listeners to expand and contract
  7423. * stacks.
  7424. * @private
  7425. */
  7426. FlagsSeries.prototype.drawTracker = function () {
  7427. var series = this,
  7428. points = series.points;
  7429. _super.prototype.drawTracker.call(this);
  7430. /* *
  7431. * Bring each stacked flag up on mouse over, this allows readability
  7432. * of vertically stacked elements as well as tight points on the x
  7433. * axis. #1924.
  7434. */
  7435. points.forEach(function (point) {
  7436. var graphic = point.graphic;
  7437. if (graphic) {
  7438. if (point.unbindMouseOver) {
  7439. point.unbindMouseOver();
  7440. }
  7441. point.unbindMouseOver = addEvent(graphic.element, 'mouseover', function () {
  7442. // Raise this point
  7443. if (point.stackIndex > 0 &&
  7444. !point.raised) {
  7445. point._y = graphic.y;
  7446. graphic.attr({
  7447. y: point._y - 8
  7448. });
  7449. point.raised = true;
  7450. }
  7451. // Revert other raised points
  7452. points.forEach(function (otherPoint) {
  7453. if (otherPoint !== point &&
  7454. otherPoint.raised &&
  7455. otherPoint.graphic) {
  7456. otherPoint.graphic.attr({
  7457. y: otherPoint._y
  7458. });
  7459. otherPoint.raised = false;
  7460. }
  7461. });
  7462. });
  7463. }
  7464. });
  7465. };
  7466. /**
  7467. * Get presentational attributes
  7468. * @private
  7469. */
  7470. FlagsSeries.prototype.pointAttribs = function (point, state) {
  7471. var options = this.options,
  7472. color = (point && point.color) || this.color,
  7473. lineColor = options.lineColor,
  7474. lineWidth = (point && point.lineWidth),
  7475. fill = (point && point.fillColor) || options.fillColor;
  7476. if (state) {
  7477. fill = options.states[state].fillColor;
  7478. lineColor = options.states[state].lineColor;
  7479. lineWidth = options.states[state].lineWidth;
  7480. }
  7481. return {
  7482. fill: fill || color,
  7483. stroke: lineColor || color,
  7484. 'stroke-width': lineWidth || options.lineWidth || 0
  7485. };
  7486. };
  7487. /**
  7488. * @private
  7489. */
  7490. FlagsSeries.prototype.setClip = function () {
  7491. Series.prototype.setClip.apply(this, arguments);
  7492. if (this.options.clip !== false &&
  7493. this.sharedClipKey &&
  7494. this.markerGroup) {
  7495. this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
  7496. }
  7497. };
  7498. /**
  7499. * Flags are used to mark events in stock charts. They can be added on the
  7500. * timeline, or attached to a specific series.
  7501. *
  7502. * @sample stock/demo/flags-general/
  7503. * Flags on a line series
  7504. *
  7505. * @extends plotOptions.column
  7506. * @excluding animation, borderColor, borderRadius, borderWidth,
  7507. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  7508. * turboThreshold
  7509. * @product highstock
  7510. * @optionparent plotOptions.flags
  7511. */
  7512. FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  7513. /**
  7514. * In case the flag is placed on a series, on what point key to place
  7515. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  7516. * however, the flag can optionally be placed on the `open`, `high`,
  7517. * `low` or `close` key.
  7518. *
  7519. * @sample {highstock} stock/plotoptions/flags-onkey/
  7520. * Range series, flag on high
  7521. *
  7522. * @type {string}
  7523. * @default y
  7524. * @since 4.2.2
  7525. * @product highstock
  7526. * @validvalue ["y", "open", "high", "low", "close"]
  7527. * @apioption plotOptions.flags.onKey
  7528. */
  7529. /**
  7530. * The id of the series that the flags should be drawn on. If no id
  7531. * is given, the flags are drawn on the x axis.
  7532. *
  7533. * @sample {highstock} stock/plotoptions/flags/
  7534. * Flags on series and on x axis
  7535. *
  7536. * @type {string}
  7537. * @product highstock
  7538. * @apioption plotOptions.flags.onSeries
  7539. */
  7540. pointRange: 0,
  7541. /**
  7542. * Whether the flags are allowed to overlap sideways. If `false`, the
  7543. * flags are moved sideways using an algorithm that seeks to place every
  7544. * flag as close as possible to its original position.
  7545. *
  7546. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  7547. * Allow sideways overlap
  7548. *
  7549. * @since 6.0.4
  7550. */
  7551. allowOverlapX: false,
  7552. /**
  7553. * The shape of the marker. Can be one of "flag", "circlepin",
  7554. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  7555. * Individual shapes can also be set for each point.
  7556. *
  7557. * @sample {highstock} stock/plotoptions/flags/
  7558. * Different shapes
  7559. *
  7560. * @type {Highcharts.FlagsShapeValue}
  7561. * @product highstock
  7562. */
  7563. shape: 'flag',
  7564. /**
  7565. * When multiple flags in the same series fall on the same value, this
  7566. * number determines the vertical offset between them.
  7567. *
  7568. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  7569. * A greater stack distance
  7570. *
  7571. * @product highstock
  7572. */
  7573. stackDistance: 12,
  7574. /**
  7575. * Text alignment for the text inside the flag.
  7576. *
  7577. * @since 5.0.0
  7578. * @product highstock
  7579. * @validvalue ["left", "center", "right"]
  7580. */
  7581. textAlign: 'center',
  7582. /**
  7583. * Specific tooltip options for flag series. Flag series tooltips are
  7584. * different from most other types in that a flag doesn't have a data
  7585. * value, so the tooltip rather displays the `text` option for each
  7586. * point.
  7587. *
  7588. * @extends plotOptions.series.tooltip
  7589. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  7590. * @product highstock
  7591. */
  7592. tooltip: {
  7593. pointFormat: '{point.text}'
  7594. },
  7595. threshold: null,
  7596. /**
  7597. * The text to display on each flag. This can be defined on series
  7598. * level, or individually for each point. Defaults to `"A"`.
  7599. *
  7600. * @type {string}
  7601. * @default A
  7602. * @product highstock
  7603. * @apioption plotOptions.flags.title
  7604. */
  7605. /**
  7606. * The y position of the top left corner of the flag relative to either
  7607. * the series (if onSeries is defined), or the x axis. Defaults to
  7608. * `-30`.
  7609. *
  7610. * @product highstock
  7611. */
  7612. y: -30,
  7613. /**
  7614. * Whether to use HTML to render the flag texts. Using HTML allows for
  7615. * advanced formatting, images and reliable bi-directional text
  7616. * rendering. Note that exported images won't respect the HTML, and that
  7617. * HTML won't respect Z-index settings.
  7618. *
  7619. * @type {boolean}
  7620. * @default false
  7621. * @since 1.3
  7622. * @product highstock
  7623. * @apioption plotOptions.flags.useHTML
  7624. */
  7625. /**
  7626. * Fixed width of the flag's shape. By default, width is autocalculated
  7627. * according to the flag's title.
  7628. *
  7629. * @sample {highstock} stock/demo/flags-shapes/
  7630. * Flags with fixed width
  7631. *
  7632. * @type {number}
  7633. * @product highstock
  7634. * @apioption plotOptions.flags.width
  7635. */
  7636. /**
  7637. * Fixed height of the flag's shape. By default, height is
  7638. * autocalculated according to the flag's title.
  7639. *
  7640. * @type {number}
  7641. * @product highstock
  7642. * @apioption plotOptions.flags.height
  7643. */
  7644. /**
  7645. * The fill color for the flags.
  7646. *
  7647. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7648. * @product highstock
  7649. */
  7650. fillColor: palette.backgroundColor,
  7651. /**
  7652. * The color of the line/border of the flag.
  7653. *
  7654. * In styled mode, the stroke is set in the
  7655. * `.highcharts-flag-series.highcharts-point` rule.
  7656. *
  7657. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7658. * @default #000000
  7659. * @product highstock
  7660. * @apioption plotOptions.flags.lineColor
  7661. */
  7662. /**
  7663. * The pixel width of the flag's line/border.
  7664. *
  7665. * @product highstock
  7666. */
  7667. lineWidth: 1,
  7668. states: {
  7669. /**
  7670. * @extends plotOptions.column.states.hover
  7671. * @product highstock
  7672. */
  7673. hover: {
  7674. /**
  7675. * The color of the line/border of the flag.
  7676. *
  7677. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7678. * @product highstock
  7679. */
  7680. lineColor: palette.neutralColor100,
  7681. /**
  7682. * The fill or background color of the flag.
  7683. *
  7684. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7685. * @product highstock
  7686. */
  7687. fillColor: palette.highlightColor20
  7688. }
  7689. },
  7690. /**
  7691. * The text styles of the flag.
  7692. *
  7693. * In styled mode, the styles are set in the
  7694. * `.highcharts-flag-series .highcharts-point` rule.
  7695. *
  7696. * @type {Highcharts.CSSObject}
  7697. * @default {"fontSize": "11px", "fontWeight": "bold"}
  7698. * @product highstock
  7699. */
  7700. style: {
  7701. /** @ignore-option */
  7702. fontSize: '11px',
  7703. /** @ignore-option */
  7704. fontWeight: 'bold'
  7705. }
  7706. });
  7707. return FlagsSeries;
  7708. }(ColumnSeries));
  7709. extend(FlagsSeries.prototype, {
  7710. allowDG: false,
  7711. /**
  7712. * @private
  7713. * @function Highcharts.seriesTypes.flags#buildKDTree
  7714. */
  7715. buildKDTree: noop,
  7716. forceCrop: true,
  7717. getPlotBox: OnSeriesMixin.getPlotBox,
  7718. /**
  7719. * Inherit the initialization from base Series.
  7720. *
  7721. * @private
  7722. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  7723. */
  7724. init: Series.prototype.init,
  7725. /**
  7726. * Don't invert the flag marker group (#4960).
  7727. *
  7728. * @private
  7729. * @function Highcharts.seriesTypes.flags#invertGroups
  7730. */
  7731. invertGroups: noop,
  7732. // Flags series group should not be invertible (#14063).
  7733. invertible: false,
  7734. noSharedTooltip: true,
  7735. pointClass: FlagsPoint,
  7736. sorted: false,
  7737. takeOrdinalPosition: false,
  7738. trackerGroups: ['markerGroup'],
  7739. translate: OnSeriesMixin.translate
  7740. });
  7741. SeriesRegistry.registerSeriesType('flags', FlagsSeries);
  7742. /* *
  7743. *
  7744. * Default Export
  7745. *
  7746. * */
  7747. /* *
  7748. *
  7749. * API Declarations
  7750. *
  7751. * */
  7752. /**
  7753. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  7754. */
  7755. ''; // detach doclets above
  7756. /* *
  7757. *
  7758. * API Option
  7759. *
  7760. * */
  7761. /**
  7762. * A `flags` series. If the [type](#series.flags.type) option is not
  7763. * specified, it is inherited from [chart.type](#chart.type).
  7764. *
  7765. * @extends series,plotOptions.flags
  7766. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  7767. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  7768. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  7769. * pointWidth, step, turboThreshold, useOhlcData
  7770. * @product highstock
  7771. * @apioption series.flags
  7772. */
  7773. /**
  7774. * An array of data points for the series. For the `flags` series type,
  7775. * points can be given in the following ways:
  7776. *
  7777. * 1. An array of objects with named values. The following snippet shows only a
  7778. * few settings, see the complete options set below. If the total number of
  7779. * data points exceeds the series'
  7780. * [turboThreshold](#series.flags.turboThreshold), this option is not
  7781. * available.
  7782. * ```js
  7783. * data: [{
  7784. * x: 1,
  7785. * title: "A",
  7786. * text: "First event"
  7787. * }, {
  7788. * x: 1,
  7789. * title: "B",
  7790. * text: "Second event"
  7791. * }]
  7792. * ```
  7793. *
  7794. * @type {Array<*>}
  7795. * @extends series.line.data
  7796. * @excluding dataLabels, marker, name, y
  7797. * @product highstock
  7798. * @apioption series.flags.data
  7799. */
  7800. /**
  7801. * The fill color of an individual flag. By default it inherits from
  7802. * the series color.
  7803. *
  7804. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7805. * @product highstock
  7806. * @apioption series.flags.data.fillColor
  7807. */
  7808. /**
  7809. * The longer text to be shown in the flag's tooltip.
  7810. *
  7811. * @type {string}
  7812. * @product highstock
  7813. * @apioption series.flags.data.text
  7814. */
  7815. /**
  7816. * The short text to be shown on the flag.
  7817. *
  7818. * @type {string}
  7819. * @product highstock
  7820. * @apioption series.flags.data.title
  7821. */
  7822. ''; // adds doclets above to transpiled file
  7823. return FlagsSeries;
  7824. });
  7825. _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) {
  7826. /* *
  7827. *
  7828. * (c) 2010-2021 Torstein Honsi
  7829. *
  7830. * License: www.highcharts.com/license
  7831. *
  7832. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7833. *
  7834. * */
  7835. var defaultOptions = O.defaultOptions;
  7836. var addEvent = U.addEvent,
  7837. createElement = U.createElement,
  7838. css = U.css,
  7839. defined = U.defined,
  7840. destroyObjectProperties = U.destroyObjectProperties,
  7841. discardElement = U.discardElement,
  7842. extend = U.extend,
  7843. find = U.find,
  7844. fireEvent = U.fireEvent,
  7845. isNumber = U.isNumber,
  7846. merge = U.merge,
  7847. objectEach = U.objectEach,
  7848. pad = U.pad,
  7849. pick = U.pick,
  7850. pInt = U.pInt,
  7851. splat = U.splat;
  7852. /**
  7853. * Define the time span for the button
  7854. *
  7855. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  7856. */
  7857. /**
  7858. * Callback function to react on button clicks.
  7859. *
  7860. * @callback Highcharts.RangeSelectorClickCallbackFunction
  7861. *
  7862. * @param {global.Event} e
  7863. * Event arguments.
  7864. *
  7865. * @param {boolean|undefined}
  7866. * Return false to cancel the default button event.
  7867. */
  7868. /**
  7869. * Callback function to parse values entered in the input boxes and return a
  7870. * valid JavaScript time as milliseconds since 1970.
  7871. *
  7872. * @callback Highcharts.RangeSelectorParseCallbackFunction
  7873. *
  7874. * @param {string} value
  7875. * Input value to parse.
  7876. *
  7877. * @return {number}
  7878. * Parsed JavaScript time value.
  7879. */
  7880. /* ************************************************************************** *
  7881. * Start Range Selector code *
  7882. * ************************************************************************** */
  7883. extend(defaultOptions, {
  7884. /**
  7885. * The range selector is a tool for selecting ranges to display within
  7886. * the chart. It provides buttons to select preconfigured ranges in
  7887. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  7888. * boxes where min and max dates can be manually input.
  7889. *
  7890. * @product highstock gantt
  7891. * @optionparent rangeSelector
  7892. */
  7893. rangeSelector: {
  7894. /**
  7895. * Whether to enable all buttons from the start. By default buttons are
  7896. * only enabled if the corresponding time range exists on the X axis,
  7897. * but enabling all buttons allows for dynamically loading different
  7898. * time ranges.
  7899. *
  7900. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  7901. * All buttons enabled
  7902. *
  7903. * @since 2.0.3
  7904. */
  7905. allButtonsEnabled: false,
  7906. /**
  7907. * An array of configuration objects for the buttons.
  7908. *
  7909. * Defaults to:
  7910. * ```js
  7911. * buttons: [{
  7912. * type: 'month',
  7913. * count: 1,
  7914. * text: '1m',
  7915. * title: 'View 1 month'
  7916. * }, {
  7917. * type: 'month',
  7918. * count: 3,
  7919. * text: '3m',
  7920. * title: 'View 3 months'
  7921. * }, {
  7922. * type: 'month',
  7923. * count: 6,
  7924. * text: '6m',
  7925. * title: 'View 6 months'
  7926. * }, {
  7927. * type: 'ytd',
  7928. * text: 'YTD',
  7929. * title: 'View year to date'
  7930. * }, {
  7931. * type: 'year',
  7932. * count: 1,
  7933. * text: '1y',
  7934. * title: 'View 1 year'
  7935. * }, {
  7936. * type: 'all',
  7937. * text: 'All',
  7938. * title: 'View all'
  7939. * }]
  7940. * ```
  7941. *
  7942. * @sample {highstock} stock/rangeselector/datagrouping/
  7943. * Data grouping by buttons
  7944. *
  7945. * @type {Array<*>}
  7946. */
  7947. buttons: void 0,
  7948. /**
  7949. * How many units of the defined type the button should span. If `type`
  7950. * is "month" and `count` is 3, the button spans three months.
  7951. *
  7952. * @type {number}
  7953. * @default 1
  7954. * @apioption rangeSelector.buttons.count
  7955. */
  7956. /**
  7957. * Fires when clicking on the rangeSelector button. One parameter,
  7958. * event, is passed to the function, containing common event
  7959. * information.
  7960. *
  7961. * ```js
  7962. * click: function(e) {
  7963. * console.log(this);
  7964. * }
  7965. * ```
  7966. *
  7967. * Return false to stop default button's click action.
  7968. *
  7969. * @sample {highstock} stock/rangeselector/button-click/
  7970. * Click event on the button
  7971. *
  7972. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  7973. * @apioption rangeSelector.buttons.events.click
  7974. */
  7975. /**
  7976. * Additional range (in milliseconds) added to the end of the calculated
  7977. * time span.
  7978. *
  7979. * @sample {highstock} stock/rangeselector/min-max-offsets/
  7980. * Button offsets
  7981. *
  7982. * @type {number}
  7983. * @default 0
  7984. * @since 6.0.0
  7985. * @apioption rangeSelector.buttons.offsetMax
  7986. */
  7987. /**
  7988. * Additional range (in milliseconds) added to the start of the
  7989. * calculated time span.
  7990. *
  7991. * @sample {highstock} stock/rangeselector/min-max-offsets/
  7992. * Button offsets
  7993. *
  7994. * @type {number}
  7995. * @default 0
  7996. * @since 6.0.0
  7997. * @apioption rangeSelector.buttons.offsetMin
  7998. */
  7999. /**
  8000. * When buttons apply dataGrouping on a series, by default zooming
  8001. * in/out will deselect buttons and unset dataGrouping. Enable this
  8002. * option to keep buttons selected when extremes change.
  8003. *
  8004. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  8005. * Different preserveDataGrouping settings
  8006. *
  8007. * @type {boolean}
  8008. * @default false
  8009. * @since 6.1.2
  8010. * @apioption rangeSelector.buttons.preserveDataGrouping
  8011. */
  8012. /**
  8013. * A custom data grouping object for each button.
  8014. *
  8015. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  8016. *
  8017. * @sample {highstock} stock/rangeselector/datagrouping/
  8018. * Data grouping by range selector buttons
  8019. *
  8020. * @type {*}
  8021. * @extends plotOptions.series.dataGrouping
  8022. * @apioption rangeSelector.buttons.dataGrouping
  8023. */
  8024. /**
  8025. * The text for the button itself.
  8026. *
  8027. * @type {string}
  8028. * @apioption rangeSelector.buttons.text
  8029. */
  8030. /**
  8031. * Explanation for the button, shown as a tooltip on hover, and used by
  8032. * assistive technology.
  8033. *
  8034. * @type {string}
  8035. * @apioption rangeSelector.buttons.title
  8036. */
  8037. /**
  8038. * Defined the time span for the button. Can be one of `millisecond`,
  8039. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  8040. * and `all`.
  8041. *
  8042. * @type {Highcharts.RangeSelectorButtonTypeValue}
  8043. * @apioption rangeSelector.buttons.type
  8044. */
  8045. /**
  8046. * The space in pixels between the buttons in the range selector.
  8047. */
  8048. buttonSpacing: 5,
  8049. /**
  8050. * Whether to collapse the range selector buttons into a dropdown when
  8051. * there is not enough room to show everything in a single row, instead
  8052. * of dividing the range selector into multiple rows.
  8053. * Can be one of the following:
  8054. * - `always`: Always collapse
  8055. * - `responsive`: Only collapse when there is not enough room
  8056. * - `never`: Never collapse
  8057. *
  8058. * @sample {highstock} stock/rangeselector/dropdown/
  8059. * Dropdown option
  8060. *
  8061. * @validvalue ["always", "responsive", "never"]
  8062. * @since 9.0.0
  8063. */
  8064. dropdown: 'responsive',
  8065. /**
  8066. * Enable or disable the range selector. Default to `true` for stock
  8067. * charts, using the `stockChart` factory.
  8068. *
  8069. * @sample {highstock} stock/rangeselector/enabled/
  8070. * Disable the range selector
  8071. *
  8072. * @type {boolean|undefined}
  8073. * @default {highstock} true
  8074. */
  8075. enabled: void 0,
  8076. /**
  8077. * The vertical alignment of the rangeselector box. Allowed properties
  8078. * are `top`, `middle`, `bottom`.
  8079. *
  8080. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  8081. * Middle
  8082. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  8083. * Bottom
  8084. *
  8085. * @type {Highcharts.VerticalAlignValue}
  8086. * @since 6.0.0
  8087. */
  8088. verticalAlign: 'top',
  8089. /**
  8090. * A collection of attributes for the buttons. The object takes SVG
  8091. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  8092. * a collection of CSS properties for the text.
  8093. *
  8094. * The object can also be extended with states, so you can set
  8095. * presentational options for `hover`, `select` or `disabled` button
  8096. * states.
  8097. *
  8098. * CSS styles for the text label.
  8099. *
  8100. * In styled mode, the buttons are styled by the
  8101. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  8102. * different states.
  8103. *
  8104. * @sample {highstock} stock/rangeselector/styling/
  8105. * Styling the buttons and inputs
  8106. *
  8107. * @type {Highcharts.SVGAttributes}
  8108. */
  8109. buttonTheme: {
  8110. /** @ignore */
  8111. width: 28,
  8112. /** @ignore */
  8113. height: 18,
  8114. /** @ignore */
  8115. padding: 2,
  8116. /** @ignore */
  8117. zIndex: 7 // #484, #852
  8118. },
  8119. /**
  8120. * When the rangeselector is floating, the plot area does not reserve
  8121. * space for it. This opens for positioning anywhere on the chart.
  8122. *
  8123. * @sample {highstock} stock/rangeselector/floating/
  8124. * Placing the range selector between the plot area and the
  8125. * navigator
  8126. *
  8127. * @since 6.0.0
  8128. */
  8129. floating: false,
  8130. /**
  8131. * The x offset of the range selector relative to its horizontal
  8132. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8133. *
  8134. * @since 6.0.0
  8135. */
  8136. x: 0,
  8137. /**
  8138. * The y offset of the range selector relative to its horizontal
  8139. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8140. *
  8141. * @since 6.0.0
  8142. */
  8143. y: 0,
  8144. /**
  8145. * Deprecated. The height of the range selector. Currently it is
  8146. * calculated dynamically.
  8147. *
  8148. * @deprecated
  8149. * @type {number|undefined}
  8150. * @since 2.1.9
  8151. */
  8152. height: void 0,
  8153. /**
  8154. * The border color of the date input boxes.
  8155. *
  8156. * @sample {highstock} stock/rangeselector/styling/
  8157. * Styling the buttons and inputs
  8158. *
  8159. * @type {Highcharts.ColorString}
  8160. * @since 1.3.7
  8161. */
  8162. inputBoxBorderColor: 'none',
  8163. /**
  8164. * The pixel height of the date input boxes.
  8165. *
  8166. * @sample {highstock} stock/rangeselector/styling/
  8167. * Styling the buttons and inputs
  8168. *
  8169. * @since 1.3.7
  8170. */
  8171. inputBoxHeight: 17,
  8172. /**
  8173. * The pixel width of the date input boxes. When `undefined`, the width
  8174. * is fitted to the rendered content.
  8175. *
  8176. * @sample {highstock} stock/rangeselector/styling/
  8177. * Styling the buttons and inputs
  8178. *
  8179. * @type {number|undefined}
  8180. * @since 1.3.7
  8181. */
  8182. inputBoxWidth: void 0,
  8183. /**
  8184. * The date format in the input boxes when not selected for editing.
  8185. * Defaults to `%b %e, %Y`.
  8186. *
  8187. * This is used to determine which type of input to show,
  8188. * `datetime-local`, `date` or `time` and falling back to `text` when
  8189. * the browser does not support the input type or the format contains
  8190. * milliseconds.
  8191. *
  8192. * @sample {highstock} stock/rangeselector/input-type/
  8193. * Input types
  8194. * @sample {highstock} stock/rangeselector/input-format/
  8195. * Milliseconds in the range selector
  8196. *
  8197. */
  8198. inputDateFormat: '%b %e, %Y',
  8199. /**
  8200. * A custom callback function to parse values entered in the input boxes
  8201. * and return a valid JavaScript time as milliseconds since 1970.
  8202. * The first argument passed is a value to parse,
  8203. * second is a boolean indicating use of the UTC time.
  8204. *
  8205. * This will only get called for inputs of type `text`. Since v8.2.3,
  8206. * the input type is dynamically determined based on the granularity
  8207. * of the `inputDateFormat` and the browser support.
  8208. *
  8209. * @sample {highstock} stock/rangeselector/input-format/
  8210. * Milliseconds in the range selector
  8211. *
  8212. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  8213. * @since 1.3.3
  8214. */
  8215. inputDateParser: void 0,
  8216. /**
  8217. * The date format in the input boxes when they are selected for
  8218. * editing. This must be a format that is recognized by JavaScript
  8219. * Date.parse.
  8220. *
  8221. * This will only be used for inputs of type `text`. Since v8.2.3,
  8222. * the input type is dynamically determined based on the granularity
  8223. * of the `inputDateFormat` and the browser support.
  8224. *
  8225. * @sample {highstock} stock/rangeselector/input-format/
  8226. * Milliseconds in the range selector
  8227. *
  8228. */
  8229. inputEditDateFormat: '%Y-%m-%d',
  8230. /**
  8231. * Enable or disable the date input boxes.
  8232. */
  8233. inputEnabled: true,
  8234. /**
  8235. * Positioning for the input boxes. Allowed properties are `align`,
  8236. * `x` and `y`.
  8237. *
  8238. * @since 1.2.4
  8239. */
  8240. inputPosition: {
  8241. /**
  8242. * The alignment of the input box. Allowed properties are `left`,
  8243. * `center`, `right`.
  8244. *
  8245. * @sample {highstock} stock/rangeselector/input-button-position/
  8246. * Alignment
  8247. *
  8248. * @type {Highcharts.AlignValue}
  8249. * @since 6.0.0
  8250. */
  8251. align: 'right',
  8252. /**
  8253. * X offset of the input row.
  8254. */
  8255. x: 0,
  8256. /**
  8257. * Y offset of the input row.
  8258. */
  8259. y: 0
  8260. },
  8261. /**
  8262. * The space in pixels between the labels and the date input boxes in
  8263. * the range selector.
  8264. *
  8265. * @since 9.0.0
  8266. */
  8267. inputSpacing: 5,
  8268. /**
  8269. * The index of the button to appear pre-selected.
  8270. *
  8271. * @type {number}
  8272. */
  8273. selected: void 0,
  8274. /**
  8275. * Positioning for the button row.
  8276. *
  8277. * @since 1.2.4
  8278. */
  8279. buttonPosition: {
  8280. /**
  8281. * The alignment of the input box. Allowed properties are `left`,
  8282. * `center`, `right`.
  8283. *
  8284. * @sample {highstock} stock/rangeselector/input-button-position/
  8285. * Alignment
  8286. *
  8287. * @type {Highcharts.AlignValue}
  8288. * @since 6.0.0
  8289. */
  8290. align: 'left',
  8291. /**
  8292. * X offset of the button row.
  8293. */
  8294. x: 0,
  8295. /**
  8296. * Y offset of the button row.
  8297. */
  8298. y: 0
  8299. },
  8300. /**
  8301. * CSS for the HTML inputs in the range selector.
  8302. *
  8303. * In styled mode, the inputs are styled by the
  8304. * `.highcharts-range-input text` rule in SVG mode, and
  8305. * `input.highcharts-range-selector` when active.
  8306. *
  8307. * @sample {highstock} stock/rangeselector/styling/
  8308. * Styling the buttons and inputs
  8309. *
  8310. * @type {Highcharts.CSSObject}
  8311. * @apioption rangeSelector.inputStyle
  8312. */
  8313. inputStyle: {
  8314. /** @ignore */
  8315. color: palette.highlightColor80,
  8316. /** @ignore */
  8317. cursor: 'pointer'
  8318. },
  8319. /**
  8320. * CSS styles for the labels - the Zoom, From and To texts.
  8321. *
  8322. * In styled mode, the labels are styled by the
  8323. * `.highcharts-range-label` class.
  8324. *
  8325. * @sample {highstock} stock/rangeselector/styling/
  8326. * Styling the buttons and inputs
  8327. *
  8328. * @type {Highcharts.CSSObject}
  8329. */
  8330. labelStyle: {
  8331. /** @ignore */
  8332. color: palette.neutralColor60
  8333. }
  8334. }
  8335. });
  8336. extend(defaultOptions.lang,
  8337. /**
  8338. * Language object. The language object is global and it can't be set
  8339. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  8340. * set it before any chart is initialized.
  8341. *
  8342. * ```js
  8343. * Highcharts.setOptions({
  8344. * lang: {
  8345. * months: [
  8346. * 'Janvier', 'Février', 'Mars', 'Avril',
  8347. * 'Mai', 'Juin', 'Juillet', 'Août',
  8348. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  8349. * ],
  8350. * weekdays: [
  8351. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  8352. * 'Jeudi', 'Vendredi', 'Samedi'
  8353. * ]
  8354. * }
  8355. * });
  8356. * ```
  8357. *
  8358. * @optionparent lang
  8359. */
  8360. {
  8361. /**
  8362. * The text for the label for the range selector buttons.
  8363. *
  8364. * @product highstock gantt
  8365. */
  8366. rangeSelectorZoom: 'Zoom',
  8367. /**
  8368. * The text for the label for the "from" input box in the range
  8369. * selector. Since v9.0, this string is empty as the label is not
  8370. * rendered by default.
  8371. *
  8372. * @product highstock gantt
  8373. */
  8374. rangeSelectorFrom: '',
  8375. /**
  8376. * The text for the label for the "to" input box in the range selector.
  8377. *
  8378. * @product highstock gantt
  8379. */
  8380. rangeSelectorTo: '→'
  8381. });
  8382. /* eslint-disable no-invalid-this, valid-jsdoc */
  8383. /**
  8384. * The range selector.
  8385. *
  8386. * @private
  8387. * @class
  8388. * @name Highcharts.RangeSelector
  8389. * @param {Highcharts.Chart} chart
  8390. */
  8391. var RangeSelector = /** @class */ (function () {
  8392. function RangeSelector(chart) {
  8393. /* *
  8394. *
  8395. * Properties
  8396. *
  8397. * */
  8398. this.buttons = void 0;
  8399. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  8400. this.initialButtonGroupWidth = 0;
  8401. this.options = void 0;
  8402. this.chart = chart;
  8403. // Run RangeSelector
  8404. this.init(chart);
  8405. }
  8406. /**
  8407. * The method to run when one of the buttons in the range selectors is
  8408. * clicked
  8409. *
  8410. * @private
  8411. * @function Highcharts.RangeSelector#clickButton
  8412. * @param {number} i
  8413. * The index of the button
  8414. * @param {boolean} [redraw]
  8415. * @return {void}
  8416. */
  8417. RangeSelector.prototype.clickButton = function (i, redraw) {
  8418. var rangeSelector = this,
  8419. chart = rangeSelector.chart,
  8420. rangeOptions = rangeSelector.buttonOptions[i],
  8421. baseAxis = chart.xAxis[0],
  8422. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  8423. dataMin = unionExtremes.dataMin,
  8424. dataMax = unionExtremes.dataMax,
  8425. newMin,
  8426. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  8427. pick(dataMax,
  8428. baseAxis.max))), // #1568
  8429. type = rangeOptions.type,
  8430. baseXAxisOptions,
  8431. range = rangeOptions._range,
  8432. rangeMin,
  8433. minSetting,
  8434. rangeSetting,
  8435. ctx,
  8436. ytdExtremes,
  8437. dataGrouping = rangeOptions.dataGrouping;
  8438. // chart has no data, base series is removed
  8439. if (dataMin === null || dataMax === null) {
  8440. return;
  8441. }
  8442. // Set the fixed range before range is altered
  8443. chart.fixedRange = range;
  8444. rangeSelector.setSelected(i);
  8445. // Apply dataGrouping associated to button
  8446. if (dataGrouping) {
  8447. this.forcedDataGrouping = true;
  8448. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  8449. this.frozenStates = rangeOptions.preserveDataGrouping;
  8450. }
  8451. // Apply range
  8452. if (type === 'month' || type === 'year') {
  8453. if (!baseAxis) {
  8454. // This is set to the user options and picked up later when the
  8455. // axis is instantiated so that we know the min and max.
  8456. range = rangeOptions;
  8457. }
  8458. else {
  8459. ctx = {
  8460. range: rangeOptions,
  8461. max: newMax,
  8462. chart: chart,
  8463. dataMin: dataMin,
  8464. dataMax: dataMax
  8465. };
  8466. newMin = baseAxis.minFromRange.call(ctx);
  8467. if (isNumber(ctx.newMax)) {
  8468. newMax = ctx.newMax;
  8469. }
  8470. }
  8471. // Fixed times like minutes, hours, days
  8472. }
  8473. else if (range) {
  8474. newMin = Math.max(newMax - range, dataMin);
  8475. newMax = Math.min(newMin + range, dataMax);
  8476. }
  8477. else if (type === 'ytd') {
  8478. // On user clicks on the buttons, or a delayed action running from
  8479. // the beforeRender event (below), the baseAxis is defined.
  8480. if (baseAxis) {
  8481. // When "ytd" is the pre-selected button for the initial view,
  8482. // its calculation is delayed and rerun in the beforeRender
  8483. // event (below). When the series are initialized, but before
  8484. // the chart is rendered, we have access to the xData array
  8485. // (#942).
  8486. if (typeof dataMax === 'undefined') {
  8487. dataMin = Number.MAX_VALUE;
  8488. dataMax = Number.MIN_VALUE;
  8489. chart.series.forEach(function (series) {
  8490. // reassign it to the last item
  8491. var xData = series.xData;
  8492. dataMin = Math.min(xData[0], dataMin);
  8493. dataMax = Math.max(xData[xData.length - 1], dataMax);
  8494. });
  8495. redraw = false;
  8496. }
  8497. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  8498. newMin = rangeMin = ytdExtremes.min;
  8499. newMax = ytdExtremes.max;
  8500. // "ytd" is pre-selected. We don't yet have access to processed
  8501. // point and extremes data (things like pointStart and pointInterval
  8502. // are missing), so we delay the process (#942)
  8503. }
  8504. else {
  8505. rangeSelector.deferredYTDClick = i;
  8506. return;
  8507. }
  8508. }
  8509. else if (type === 'all' && baseAxis) {
  8510. newMin = dataMin;
  8511. newMax = dataMax;
  8512. }
  8513. if (defined(newMin)) {
  8514. newMin += rangeOptions._offsetMin;
  8515. }
  8516. if (defined(newMax)) {
  8517. newMax += rangeOptions._offsetMax;
  8518. }
  8519. if (this.dropdown) {
  8520. this.dropdown.selectedIndex = i + 1;
  8521. }
  8522. // Update the chart
  8523. if (!baseAxis) {
  8524. // Axis not yet instanciated. Temporarily set min and range
  8525. // options and remove them on chart load (#4317).
  8526. baseXAxisOptions = splat(chart.options.xAxis)[0];
  8527. rangeSetting = baseXAxisOptions.range;
  8528. baseXAxisOptions.range = range;
  8529. minSetting = baseXAxisOptions.min;
  8530. baseXAxisOptions.min = rangeMin;
  8531. addEvent(chart, 'load', function resetMinAndRange() {
  8532. baseXAxisOptions.range = rangeSetting;
  8533. baseXAxisOptions.min = minSetting;
  8534. });
  8535. }
  8536. else {
  8537. // Existing axis object. Set extremes after render time.
  8538. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  8539. {
  8540. trigger: 'rangeSelectorButton',
  8541. rangeSelectorButton: rangeOptions
  8542. });
  8543. }
  8544. fireEvent(this, 'afterBtnClick');
  8545. };
  8546. /**
  8547. * Set the selected option. This method only sets the internal flag, it
  8548. * doesn't update the buttons or the actual zoomed range.
  8549. *
  8550. * @private
  8551. * @function Highcharts.RangeSelector#setSelected
  8552. * @param {number} [selected]
  8553. * @return {void}
  8554. */
  8555. RangeSelector.prototype.setSelected = function (selected) {
  8556. this.selected = this.options.selected = selected;
  8557. };
  8558. /**
  8559. * Initialize the range selector
  8560. *
  8561. * @private
  8562. * @function Highcharts.RangeSelector#init
  8563. * @param {Highcharts.Chart} chart
  8564. * @return {void}
  8565. */
  8566. RangeSelector.prototype.init = function (chart) {
  8567. var rangeSelector = this,
  8568. options = chart.options.rangeSelector,
  8569. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  8570. selectedOption = options.selected,
  8571. blurInputs = function () {
  8572. var minInput = rangeSelector.minInput,
  8573. maxInput = rangeSelector.maxInput;
  8574. // #3274 in some case blur is not defined
  8575. if (minInput && minInput.blur) {
  8576. fireEvent(minInput, 'blur');
  8577. }
  8578. if (maxInput && maxInput.blur) {
  8579. fireEvent(maxInput, 'blur');
  8580. }
  8581. };
  8582. rangeSelector.chart = chart;
  8583. rangeSelector.options = options;
  8584. rangeSelector.buttons = [];
  8585. rangeSelector.buttonOptions = buttonOptions;
  8586. this.eventsToUnbind = [];
  8587. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  8588. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  8589. // Extend the buttonOptions with actual range
  8590. buttonOptions.forEach(rangeSelector.computeButtonRange);
  8591. // zoomed range based on a pre-selected button index
  8592. if (typeof selectedOption !== 'undefined' &&
  8593. buttonOptions[selectedOption]) {
  8594. this.clickButton(selectedOption, false);
  8595. }
  8596. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  8597. // If a data grouping is applied to the current button, release it
  8598. // when extremes change
  8599. if (chart.xAxis && chart.xAxis[0]) {
  8600. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  8601. if (this.max - this.min !==
  8602. chart.fixedRange &&
  8603. e.trigger !== 'rangeSelectorButton' &&
  8604. e.trigger !== 'updatedData' &&
  8605. rangeSelector.forcedDataGrouping &&
  8606. !rangeSelector.frozenStates) {
  8607. this.setDataGrouping(false, false);
  8608. }
  8609. });
  8610. }
  8611. }));
  8612. };
  8613. /**
  8614. * Dynamically update the range selector buttons after a new range has been
  8615. * set
  8616. *
  8617. * @private
  8618. * @function Highcharts.RangeSelector#updateButtonStates
  8619. * @return {void}
  8620. */
  8621. RangeSelector.prototype.updateButtonStates = function () {
  8622. var rangeSelector = this,
  8623. chart = this.chart,
  8624. dropdown = this.dropdown,
  8625. baseAxis = chart.xAxis[0],
  8626. actualRange = Math.round(baseAxis.max - baseAxis.min),
  8627. hasNoData = !baseAxis.hasVisibleSeries,
  8628. day = 24 * 36e5, // A single day in milliseconds
  8629. unionExtremes = (chart.scroller &&
  8630. chart.scroller.getUnionExtremes()) || baseAxis,
  8631. dataMin = unionExtremes.dataMin,
  8632. dataMax = unionExtremes.dataMax,
  8633. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  8634. dataMin,
  8635. chart.time.useUTC),
  8636. ytdMin = ytdExtremes.min,
  8637. ytdMax = ytdExtremes.max,
  8638. selected = rangeSelector.selected,
  8639. selectedExists = isNumber(selected),
  8640. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  8641. buttons = rangeSelector.buttons;
  8642. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  8643. var range = rangeOptions._range,
  8644. type = rangeOptions.type,
  8645. count = rangeOptions.count || 1,
  8646. button = buttons[i],
  8647. state = 0,
  8648. disable,
  8649. select,
  8650. offsetRange = rangeOptions._offsetMax -
  8651. rangeOptions._offsetMin,
  8652. isSelected = i === selected,
  8653. // Disable buttons where the range exceeds what is allowed in
  8654. // the current view
  8655. isTooGreatRange = range >
  8656. dataMax - dataMin,
  8657. // Disable buttons where the range is smaller than the minimum
  8658. // range
  8659. isTooSmallRange = range < baseAxis.minRange,
  8660. // Do not select the YTD button if not explicitly told so
  8661. isYTDButNotSelected = false,
  8662. // Disable the All button if we're already showing all
  8663. isAllButAlreadyShowingAll = false,
  8664. isSameRange = range === actualRange;
  8665. // Months and years have a variable range so we check the extremes
  8666. if ((type === 'month' || type === 'year') &&
  8667. (actualRange + 36e5 >=
  8668. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  8669. (actualRange - 36e5 <=
  8670. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  8671. isSameRange = true;
  8672. }
  8673. else if (type === 'ytd') {
  8674. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  8675. isYTDButNotSelected = !isSelected;
  8676. }
  8677. else if (type === 'all') {
  8678. isSameRange = (baseAxis.max - baseAxis.min >=
  8679. dataMax - dataMin);
  8680. isAllButAlreadyShowingAll = (!isSelected &&
  8681. selectedExists &&
  8682. isSameRange);
  8683. }
  8684. // The new zoom area happens to match the range for a button - mark
  8685. // it selected. This happens when scrolling across an ordinal gap.
  8686. // It can be seen in the intraday demos when selecting 1h and scroll
  8687. // across the night gap.
  8688. disable = (!allButtonsEnabled &&
  8689. (isTooGreatRange ||
  8690. isTooSmallRange ||
  8691. isAllButAlreadyShowingAll ||
  8692. hasNoData));
  8693. select = ((isSelected && isSameRange) ||
  8694. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  8695. (isSelected && rangeSelector.frozenStates));
  8696. if (disable) {
  8697. state = 3;
  8698. }
  8699. else if (select) {
  8700. selectedExists = true; // Only one button can be selected
  8701. state = 2;
  8702. }
  8703. // If state has changed, update the button
  8704. if (button.state !== state) {
  8705. button.setState(state);
  8706. if (dropdown) {
  8707. dropdown.options[i + 1].disabled = disable;
  8708. if (state === 2) {
  8709. dropdown.selectedIndex = i + 1;
  8710. }
  8711. }
  8712. // Reset (#9209)
  8713. if (state === 0 && selected === i) {
  8714. rangeSelector.setSelected();
  8715. }
  8716. }
  8717. });
  8718. };
  8719. /**
  8720. * Compute and cache the range for an individual button
  8721. *
  8722. * @private
  8723. * @function Highcharts.RangeSelector#computeButtonRange
  8724. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  8725. * @return {void}
  8726. */
  8727. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  8728. var type = rangeOptions.type,
  8729. count = rangeOptions.count || 1,
  8730. // these time intervals have a fixed number of milliseconds, as
  8731. // opposed to month, ytd and year
  8732. fixedTimes = {
  8733. millisecond: 1,
  8734. second: 1000,
  8735. minute: 60 * 1000,
  8736. hour: 3600 * 1000,
  8737. day: 24 * 3600 * 1000,
  8738. week: 7 * 24 * 3600 * 1000
  8739. };
  8740. // Store the range on the button object
  8741. if (fixedTimes[type]) {
  8742. rangeOptions._range = fixedTimes[type] * count;
  8743. }
  8744. else if (type === 'month' || type === 'year') {
  8745. rangeOptions._range = {
  8746. month: 30,
  8747. year: 365
  8748. }[type] * 24 * 36e5 * count;
  8749. }
  8750. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  8751. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  8752. rangeOptions._range +=
  8753. rangeOptions._offsetMax - rangeOptions._offsetMin;
  8754. };
  8755. /**
  8756. * Get the unix timestamp of a HTML input for the dates
  8757. *
  8758. * @private
  8759. * @function Highcharts.RangeSelector#getInputValue
  8760. * @param {string} name
  8761. * @return {number}
  8762. */
  8763. RangeSelector.prototype.getInputValue = function (name) {
  8764. var input = name === 'min' ? this.minInput : this.maxInput;
  8765. var options = this.chart.options.rangeSelector;
  8766. var time = this.chart.time;
  8767. if (input) {
  8768. return ((input.type === 'text' && options.inputDateParser) ||
  8769. this.defaultInputDateParser)(input.value, time.useUTC, time);
  8770. }
  8771. return 0;
  8772. };
  8773. /**
  8774. * Set the internal and displayed value of a HTML input for the dates
  8775. *
  8776. * @private
  8777. * @function Highcharts.RangeSelector#setInputValue
  8778. * @param {string} name
  8779. * @param {number} [inputTime]
  8780. * @return {void}
  8781. */
  8782. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  8783. var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8784. if (input) {
  8785. var hcTimeAttr = input.getAttribute('data-hc-time');
  8786. var updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  8787. if (defined(inputTime)) {
  8788. var previousTime = updatedTime;
  8789. if (defined(previousTime)) {
  8790. input.setAttribute('data-hc-time-previous', previousTime);
  8791. }
  8792. input.setAttribute('data-hc-time', inputTime);
  8793. updatedTime = inputTime;
  8794. }
  8795. input.value = time.dateFormat(this.inputTypeFormats[input.type] || options.inputEditDateFormat, updatedTime);
  8796. if (dateBox) {
  8797. dateBox.attr({
  8798. text: time.dateFormat(options.inputDateFormat, updatedTime)
  8799. });
  8800. }
  8801. }
  8802. };
  8803. /**
  8804. * Set the min and max value of a HTML input for the dates
  8805. *
  8806. * @private
  8807. * @function Highcharts.RangeSelector#setInputExtremes
  8808. * @param {string} name
  8809. * @param {number} min
  8810. * @param {number} max
  8811. * @return {void}
  8812. */
  8813. RangeSelector.prototype.setInputExtremes = function (name, min, max) {
  8814. var input = name === 'min' ? this.minInput : this.maxInput;
  8815. if (input) {
  8816. var format = this.inputTypeFormats[input.type];
  8817. var time = this.chart.time;
  8818. if (format) {
  8819. var newMin = time.dateFormat(format,
  8820. min);
  8821. if (input.min !== newMin) {
  8822. input.min = newMin;
  8823. }
  8824. var newMax = time.dateFormat(format,
  8825. max);
  8826. if (input.max !== newMax) {
  8827. input.max = newMax;
  8828. }
  8829. }
  8830. }
  8831. };
  8832. /**
  8833. * @private
  8834. * @function Highcharts.RangeSelector#showInput
  8835. * @param {string} name
  8836. * @return {void}
  8837. */
  8838. RangeSelector.prototype.showInput = function (name) {
  8839. var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  8840. var input = name === 'min' ? this.minInput : this.maxInput;
  8841. if (input && dateBox && this.inputGroup) {
  8842. var isTextInput = input.type === 'text';
  8843. var _a = this.inputGroup,
  8844. translateX = _a.translateX,
  8845. translateY = _a.translateY;
  8846. var inputBoxWidth = this.options.inputBoxWidth;
  8847. css(input, {
  8848. width: isTextInput ? ((dateBox.width + (inputBoxWidth ? -2 : 20)) + 'px') : 'auto',
  8849. height: isTextInput ? ((dateBox.height - 2) + 'px') : 'auto',
  8850. border: '2px solid silver'
  8851. });
  8852. if (isTextInput && inputBoxWidth) {
  8853. css(input, {
  8854. left: (translateX + dateBox.x) + 'px',
  8855. top: translateY + 'px'
  8856. });
  8857. // Inputs of types date, time or datetime-local should be centered
  8858. // on top of the dateBox
  8859. }
  8860. else {
  8861. css(input, {
  8862. left: Math.min(Math.round(dateBox.x +
  8863. translateX -
  8864. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  8865. top: (translateY - 1 - (input.offsetHeight - dateBox.height) / 2) + 'px'
  8866. });
  8867. }
  8868. }
  8869. };
  8870. /**
  8871. * @private
  8872. * @function Highcharts.RangeSelector#hideInput
  8873. * @param {string} name
  8874. * @return {void}
  8875. */
  8876. RangeSelector.prototype.hideInput = function (name) {
  8877. var input = name === 'min' ? this.minInput : this.maxInput;
  8878. if (input) {
  8879. css(input, {
  8880. top: '-9999em',
  8881. border: 0,
  8882. width: '1px',
  8883. height: '1px'
  8884. });
  8885. }
  8886. };
  8887. /**
  8888. * @private
  8889. * @function Highcharts.RangeSelector#defaultInputDateParser
  8890. */
  8891. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
  8892. var hasTimezone = function (str) {
  8893. return str.length > 6 &&
  8894. (str.lastIndexOf('-') === str.length - 6 ||
  8895. str.lastIndexOf('+') === str.length - 6);
  8896. };
  8897. var input = inputDate.split('/').join('-').split(' ').join('T');
  8898. if (input.indexOf('T') === -1) {
  8899. input += 'T00:00';
  8900. }
  8901. if (useUTC) {
  8902. input += 'Z';
  8903. }
  8904. else if (H.isSafari && !hasTimezone(input)) {
  8905. var offset = new Date(input).getTimezoneOffset() / 60;
  8906. input += offset <= 0 ? "+" + pad(-offset) + ":00" : "-" + pad(offset) + ":00";
  8907. }
  8908. var date = Date.parse(input);
  8909. // If the value isn't parsed directly to a value by the
  8910. // browser's Date.parse method, like YYYY-MM-DD in IE8, try
  8911. // parsing it a different way
  8912. if (!isNumber(date)) {
  8913. var parts = inputDate.split('-');
  8914. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  8915. }
  8916. if (time && useUTC && isNumber(date)) {
  8917. date += time.getTimezoneOffset(date);
  8918. }
  8919. return date;
  8920. };
  8921. /**
  8922. * Draw either the 'from' or the 'to' HTML input box of the range selector
  8923. *
  8924. * @private
  8925. * @function Highcharts.RangeSelector#drawInput
  8926. * @param {string} name
  8927. * @return {RangeSelectorInputElements}
  8928. */
  8929. RangeSelector.prototype.drawInput = function (name) {
  8930. var _a = this,
  8931. chart = _a.chart,
  8932. div = _a.div,
  8933. inputGroup = _a.inputGroup;
  8934. var rangeSelector = this,
  8935. chartStyle = chart.renderer.style || {},
  8936. renderer = chart.renderer,
  8937. options = chart.options.rangeSelector,
  8938. lang = defaultOptions.lang,
  8939. isMin = name === 'min';
  8940. /**
  8941. * @private
  8942. */
  8943. function updateExtremes() {
  8944. var value = rangeSelector.getInputValue(name),
  8945. chartAxis = chart.xAxis[0],
  8946. dataAxis = chart.scroller && chart.scroller.xAxis ?
  8947. chart.scroller.xAxis :
  8948. chartAxis,
  8949. dataMin = dataAxis.dataMin,
  8950. dataMax = dataAxis.dataMax;
  8951. var maxInput = rangeSelector.maxInput,
  8952. minInput = rangeSelector.minInput;
  8953. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  8954. isNumber(value)) {
  8955. input.setAttribute('data-hc-time-previous', value);
  8956. // Validate the extremes. If it goes beyound the data min or
  8957. // max, use the actual data extreme (#2438).
  8958. if (isMin && maxInput && isNumber(dataMin)) {
  8959. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  8960. value = void 0;
  8961. }
  8962. else if (value < dataMin) {
  8963. value = dataMin;
  8964. }
  8965. }
  8966. else if (minInput && isNumber(dataMax)) {
  8967. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  8968. value = void 0;
  8969. }
  8970. else if (value > dataMax) {
  8971. value = dataMax;
  8972. }
  8973. }
  8974. // Set the extremes
  8975. if (typeof value !== 'undefined') { // @todo typof undefined
  8976. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  8977. }
  8978. }
  8979. }
  8980. // Create the text label
  8981. var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'];
  8982. var label = renderer
  8983. .label(text, 0)
  8984. .addClass('highcharts-range-label')
  8985. .attr({
  8986. padding: text ? 2 : 0
  8987. })
  8988. .add(inputGroup);
  8989. // Create an SVG label that shows updated date ranges and and records
  8990. // click events that bring in the HTML input.
  8991. var dateBox = renderer
  8992. .label('', 0)
  8993. .addClass('highcharts-range-input')
  8994. .attr({
  8995. padding: 2,
  8996. width: options.inputBoxWidth,
  8997. height: options.inputBoxHeight,
  8998. 'text-align': 'center'
  8999. })
  9000. .on('click',
  9001. function () {
  9002. // If it is already focused, the onfocus event doesn't fire
  9003. // (#3713)
  9004. rangeSelector.showInput(name);
  9005. rangeSelector[name + 'Input'].focus();
  9006. });
  9007. if (!chart.styledMode) {
  9008. dateBox.attr({
  9009. stroke: options.inputBoxBorderColor,
  9010. 'stroke-width': 1
  9011. });
  9012. }
  9013. dateBox.add(inputGroup);
  9014. // Create the HTML input element. This is rendered as 1x1 pixel then set
  9015. // to the right size when focused.
  9016. var input = createElement('input', {
  9017. name: name,
  9018. className: 'highcharts-range-selector'
  9019. },
  9020. void 0,
  9021. div);
  9022. // #14788: Setting input.type to an unsupported type throws in IE, so
  9023. // we need to use setAttribute instead
  9024. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%b %e, %Y'));
  9025. if (!chart.styledMode) {
  9026. // Styles
  9027. label.css(merge(chartStyle, options.labelStyle));
  9028. dateBox.css(merge({
  9029. color: palette.neutralColor80
  9030. }, chartStyle, options.inputStyle));
  9031. css(input, extend({
  9032. position: 'absolute',
  9033. border: 0,
  9034. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  9035. width: '1px',
  9036. height: '1px',
  9037. padding: 0,
  9038. textAlign: 'center',
  9039. fontSize: chartStyle.fontSize,
  9040. fontFamily: chartStyle.fontFamily,
  9041. top: '-9999em' // #4798
  9042. }, options.inputStyle));
  9043. }
  9044. // Blow up the input box
  9045. input.onfocus = function () {
  9046. rangeSelector.showInput(name);
  9047. };
  9048. // Hide away the input box
  9049. input.onblur = function () {
  9050. // update extermes only when inputs are active
  9051. if (input === H.doc.activeElement) { // Only when focused
  9052. // Update also when no `change` event is triggered, like when
  9053. // clicking inside the SVG (#4710)
  9054. updateExtremes();
  9055. }
  9056. // #10404 - move hide and blur outside focus
  9057. rangeSelector.hideInput(name);
  9058. rangeSelector.setInputValue(name);
  9059. input.blur(); // #4606
  9060. };
  9061. var keyDown = false;
  9062. // handle changes in the input boxes
  9063. input.onchange = function () {
  9064. // Update extremes and blur input when clicking date input calendar
  9065. if (!keyDown) {
  9066. updateExtremes();
  9067. rangeSelector.hideInput(name);
  9068. input.blur();
  9069. }
  9070. };
  9071. input.onkeypress = function (event) {
  9072. // IE does not fire onchange on enter
  9073. if (event.keyCode === 13) {
  9074. updateExtremes();
  9075. }
  9076. };
  9077. input.onkeydown = function (event) {
  9078. keyDown = true;
  9079. // Arrow keys
  9080. if (event.keyCode === 38 || event.keyCode === 40) {
  9081. updateExtremes();
  9082. }
  9083. };
  9084. input.onkeyup = function () {
  9085. keyDown = false;
  9086. };
  9087. return { dateBox: dateBox, input: input, label: label };
  9088. };
  9089. /**
  9090. * Get the position of the range selector buttons and inputs. This can be
  9091. * overridden from outside for custom positioning.
  9092. *
  9093. * @private
  9094. * @function Highcharts.RangeSelector#getPosition
  9095. *
  9096. * @return {Highcharts.Dictionary<number>}
  9097. */
  9098. RangeSelector.prototype.getPosition = function () {
  9099. var chart = this.chart,
  9100. options = chart.options.rangeSelector,
  9101. top = options.verticalAlign === 'top' ?
  9102. chart.plotTop - chart.axisOffset[0] :
  9103. 0; // set offset only for varticalAlign top
  9104. return {
  9105. buttonTop: top + options.buttonPosition.y,
  9106. inputTop: top + options.inputPosition.y - 10
  9107. };
  9108. };
  9109. /**
  9110. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  9111. * the current timestamp. Will choose dataMin if its value is higher than
  9112. * the timestamp for the start of current year.
  9113. *
  9114. * @private
  9115. * @function Highcharts.RangeSelector#getYTDExtremes
  9116. *
  9117. * @param {number} dataMax
  9118. *
  9119. * @param {number} dataMin
  9120. *
  9121. * @return {*}
  9122. * Returns min and max for the YTD
  9123. */
  9124. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  9125. var time = this.chart.time,
  9126. min,
  9127. now = new time.Date(dataMax),
  9128. year = time.get('FullYear',
  9129. now),
  9130. startOfYear = useUTC ?
  9131. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  9132. +new time.Date(year, 0, 1);
  9133. min = Math.max(dataMin, startOfYear);
  9134. var ts = now.getTime();
  9135. return {
  9136. max: Math.min(dataMax || ts, ts),
  9137. min: min
  9138. };
  9139. };
  9140. /**
  9141. * Render the range selector including the buttons and the inputs. The first
  9142. * time render is called, the elements are created and positioned. On
  9143. * subsequent calls, they are moved and updated.
  9144. *
  9145. * @private
  9146. * @function Highcharts.RangeSelector#render
  9147. * @param {number} [min]
  9148. * X axis minimum
  9149. * @param {number} [max]
  9150. * X axis maximum
  9151. * @return {void}
  9152. */
  9153. RangeSelector.prototype.render = function (min, max) {
  9154. var chart = this.chart,
  9155. renderer = chart.renderer,
  9156. container = chart.container,
  9157. chartOptions = chart.options,
  9158. options = chartOptions.rangeSelector,
  9159. // Place inputs above the container
  9160. inputsZIndex = pick(chartOptions.chart.style &&
  9161. chartOptions.chart.style.zIndex, 0) + 1,
  9162. inputEnabled = options.inputEnabled,
  9163. rendered = this.rendered;
  9164. if (options.enabled === false) {
  9165. return;
  9166. }
  9167. // create the elements
  9168. if (!rendered) {
  9169. this.group = renderer.g('range-selector-group')
  9170. .attr({
  9171. zIndex: 7
  9172. })
  9173. .add();
  9174. this.div = createElement('div', void 0, {
  9175. position: 'relative',
  9176. height: 0,
  9177. zIndex: inputsZIndex
  9178. });
  9179. if (this.buttonOptions.length) {
  9180. this.renderButtons();
  9181. }
  9182. // First create a wrapper outside the container in order to make
  9183. // the inputs work and make export correct
  9184. if (container.parentNode) {
  9185. container.parentNode.insertBefore(this.div, container);
  9186. }
  9187. if (inputEnabled) {
  9188. // Create the group to keep the inputs
  9189. this.inputGroup = renderer.g('input-group').add(this.group);
  9190. var minElems = this.drawInput('min');
  9191. this.minDateBox = minElems.dateBox;
  9192. this.minLabel = minElems.label;
  9193. this.minInput = minElems.input;
  9194. var maxElems = this.drawInput('max');
  9195. this.maxDateBox = maxElems.dateBox;
  9196. this.maxLabel = maxElems.label;
  9197. this.maxInput = maxElems.input;
  9198. }
  9199. }
  9200. if (inputEnabled) {
  9201. // Set or reset the input values
  9202. this.setInputValue('min', min);
  9203. this.setInputValue('max', max);
  9204. var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  9205. if (defined(unionExtremes.dataMin) && defined(unionExtremes.dataMax)) {
  9206. var minRange = chart.xAxis[0].minRange || 0;
  9207. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  9208. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  9209. }
  9210. // Reflow
  9211. if (this.inputGroup) {
  9212. var x_1 = 0;
  9213. [
  9214. this.minLabel,
  9215. this.minDateBox,
  9216. this.maxLabel,
  9217. this.maxDateBox
  9218. ].forEach(function (label) {
  9219. if (label) {
  9220. var width = label.getBBox().width;
  9221. if (width) {
  9222. label.attr({ x: x_1 });
  9223. x_1 += width + options.inputSpacing;
  9224. }
  9225. }
  9226. });
  9227. }
  9228. }
  9229. this.alignElements();
  9230. this.rendered = true;
  9231. };
  9232. /**
  9233. * Render the range buttons. This only runs the first time, later the
  9234. * positioning is laid out in alignElements.
  9235. *
  9236. * @private
  9237. * @function Highcharts.RangeSelector#renderButtons
  9238. * @return {void}
  9239. */
  9240. RangeSelector.prototype.renderButtons = function () {
  9241. var _this = this;
  9242. var _a = this,
  9243. buttons = _a.buttons,
  9244. chart = _a.chart,
  9245. options = _a.options;
  9246. var lang = defaultOptions.lang;
  9247. var renderer = chart.renderer;
  9248. var buttonTheme = merge(options.buttonTheme);
  9249. var states = buttonTheme && buttonTheme.states;
  9250. // Prevent the button from resetting the width when the button state
  9251. // changes since we need more control over the width when collapsing
  9252. // the buttons
  9253. var width = buttonTheme.width || 28;
  9254. delete buttonTheme.width;
  9255. delete buttonTheme.states;
  9256. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  9257. var dropdown = this.dropdown = createElement('select',
  9258. void 0, {
  9259. position: 'absolute',
  9260. width: '1px',
  9261. height: '1px',
  9262. padding: 0,
  9263. border: 0,
  9264. top: '-9999em',
  9265. cursor: 'pointer',
  9266. opacity: 0.0001
  9267. },
  9268. this.div);
  9269. // Prevent page zoom on iPhone
  9270. addEvent(dropdown, 'touchstart', function () {
  9271. dropdown.style.fontSize = '16px';
  9272. });
  9273. // Forward events from select to button
  9274. [
  9275. [H.isMS ? 'mouseover' : 'mouseenter'],
  9276. [H.isMS ? 'mouseout' : 'mouseleave'],
  9277. ['change', 'click']
  9278. ].forEach(function (_a) {
  9279. var from = _a[0],
  9280. to = _a[1];
  9281. addEvent(dropdown, from, function () {
  9282. var button = buttons[_this.currentButtonIndex()];
  9283. if (button) {
  9284. fireEvent(button.element, to || from);
  9285. }
  9286. });
  9287. });
  9288. this.zoomText = renderer
  9289. .text(lang.rangeSelectorZoom, 0, 15)
  9290. .add(this.buttonGroup);
  9291. if (!this.chart.styledMode) {
  9292. this.zoomText.css(options.labelStyle);
  9293. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  9294. }
  9295. createElement('option', {
  9296. textContent: this.zoomText.textStr,
  9297. disabled: true
  9298. }, void 0, dropdown);
  9299. this.buttonOptions.forEach(function (rangeOptions, i) {
  9300. createElement('option', {
  9301. textContent: rangeOptions.title || rangeOptions.text
  9302. }, void 0, dropdown);
  9303. buttons[i] = renderer
  9304. .button(rangeOptions.text, 0, 0, function (e) {
  9305. // extract events from button object and call
  9306. var buttonEvents = (rangeOptions.events &&
  9307. rangeOptions.events.click),
  9308. callDefaultEvent;
  9309. if (buttonEvents) {
  9310. callDefaultEvent =
  9311. buttonEvents.call(rangeOptions, e);
  9312. }
  9313. if (callDefaultEvent !== false) {
  9314. _this.clickButton(i);
  9315. }
  9316. _this.isActive = true;
  9317. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  9318. .attr({
  9319. 'text-align': 'center',
  9320. width: width
  9321. })
  9322. .add(_this.buttonGroup);
  9323. if (rangeOptions.title) {
  9324. buttons[i].attr('title', rangeOptions.title);
  9325. }
  9326. });
  9327. };
  9328. /**
  9329. * Align the elements horizontally and vertically.
  9330. *
  9331. * @private
  9332. * @function Highcharts.RangeSelector#alignElements
  9333. * @return {void}
  9334. */
  9335. RangeSelector.prototype.alignElements = function () {
  9336. var _this = this;
  9337. var _a = this,
  9338. buttonGroup = _a.buttonGroup,
  9339. buttons = _a.buttons,
  9340. chart = _a.chart,
  9341. group = _a.group,
  9342. inputGroup = _a.inputGroup,
  9343. options = _a.options,
  9344. zoomText = _a.zoomText;
  9345. var chartOptions = chart.options;
  9346. var navButtonOptions = (chartOptions.exporting &&
  9347. chartOptions.exporting.enabled !== false &&
  9348. chartOptions.navigation &&
  9349. chartOptions.navigation.buttonOptions);
  9350. var buttonPosition = options.buttonPosition,
  9351. inputPosition = options.inputPosition,
  9352. verticalAlign = options.verticalAlign;
  9353. // Get the X offset required to avoid overlapping with the exporting
  9354. // button. This is is used both by the buttonGroup and the inputGroup.
  9355. var getXOffsetForExportButton = function (group,
  9356. position) {
  9357. if (navButtonOptions &&
  9358. _this.titleCollision(chart) &&
  9359. verticalAlign === 'top' &&
  9360. position.align === 'right' && ((position.y -
  9361. group.getBBox().height - 12) <
  9362. ((navButtonOptions.y || 0) +
  9363. (navButtonOptions.height || 0) +
  9364. chart.spacing[0]))) {
  9365. return -40;
  9366. }
  9367. return 0;
  9368. };
  9369. var plotLeft = chart.plotLeft;
  9370. if (group && buttonPosition && inputPosition) {
  9371. var translateX = buttonPosition.x - chart.spacing[3];
  9372. if (buttonGroup) {
  9373. this.positionButtons();
  9374. if (!this.initialButtonGroupWidth) {
  9375. var width_1 = 0;
  9376. if (zoomText) {
  9377. width_1 += zoomText.getBBox().width + 5;
  9378. }
  9379. buttons.forEach(function (button, i) {
  9380. width_1 += button.width;
  9381. if (i !== buttons.length - 1) {
  9382. width_1 += options.buttonSpacing;
  9383. }
  9384. });
  9385. this.initialButtonGroupWidth = width_1;
  9386. }
  9387. plotLeft -= chart.spacing[3];
  9388. this.updateButtonStates();
  9389. // Detect collision between button group and exporting
  9390. var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
  9391. buttonPosition);
  9392. this.alignButtonGroup(xOffsetForExportButton_1);
  9393. // Skip animation
  9394. group.placed = buttonGroup.placed = chart.hasLoaded;
  9395. }
  9396. var xOffsetForExportButton = 0;
  9397. if (inputGroup) {
  9398. // Detect collision between the input group and exporting button
  9399. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  9400. if (inputPosition.align === 'left') {
  9401. translateX = plotLeft;
  9402. }
  9403. else if (inputPosition.align === 'right') {
  9404. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  9405. }
  9406. // Update the alignment to the updated spacing box
  9407. inputGroup.align({
  9408. y: inputPosition.y,
  9409. width: inputGroup.getBBox().width,
  9410. align: inputPosition.align,
  9411. // fix wrong getBBox() value on right align
  9412. x: inputPosition.x + translateX - 2
  9413. }, true, chart.spacingBox);
  9414. // Skip animation
  9415. inputGroup.placed = chart.hasLoaded;
  9416. }
  9417. this.handleCollision(xOffsetForExportButton);
  9418. // Vertical align
  9419. group.align({
  9420. verticalAlign: verticalAlign
  9421. }, true, chart.spacingBox);
  9422. var alignTranslateY = group.alignAttr.translateY;
  9423. // Set position
  9424. var groupHeight = group.getBBox().height + 20; // # 20 padding
  9425. var translateY = 0;
  9426. // Calculate bottom position
  9427. if (verticalAlign === 'bottom') {
  9428. var legendOptions = chart.legend && chart.legend.options;
  9429. var legendHeight = (legendOptions &&
  9430. legendOptions.verticalAlign === 'bottom' &&
  9431. legendOptions.enabled &&
  9432. !legendOptions.floating ?
  9433. (chart.legend.legendHeight +
  9434. pick(legendOptions.margin, 10)) :
  9435. 0);
  9436. groupHeight = groupHeight + legendHeight - 20;
  9437. translateY = (alignTranslateY -
  9438. groupHeight -
  9439. (options.floating ? 0 : options.y) -
  9440. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  9441. 10 // 10 spacing
  9442. );
  9443. }
  9444. if (verticalAlign === 'top') {
  9445. if (options.floating) {
  9446. translateY = 0;
  9447. }
  9448. if (chart.titleOffset && chart.titleOffset[0]) {
  9449. translateY = chart.titleOffset[0];
  9450. }
  9451. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  9452. }
  9453. else if (verticalAlign === 'middle') {
  9454. if (inputPosition.y === buttonPosition.y) {
  9455. translateY = alignTranslateY;
  9456. }
  9457. else if (inputPosition.y || buttonPosition.y) {
  9458. if (inputPosition.y < 0 ||
  9459. buttonPosition.y < 0) {
  9460. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  9461. }
  9462. else {
  9463. translateY = alignTranslateY - groupHeight;
  9464. }
  9465. }
  9466. }
  9467. group.translate(options.x, options.y + Math.floor(translateY));
  9468. // Translate HTML inputs
  9469. var _b = this,
  9470. minInput = _b.minInput,
  9471. maxInput = _b.maxInput,
  9472. dropdown = _b.dropdown;
  9473. if (options.inputEnabled && minInput && maxInput) {
  9474. minInput.style.marginTop = group.translateY + 'px';
  9475. maxInput.style.marginTop = group.translateY + 'px';
  9476. }
  9477. if (dropdown) {
  9478. dropdown.style.marginTop = group.translateY + 'px';
  9479. }
  9480. }
  9481. };
  9482. /**
  9483. * Align the button group horizontally and vertically.
  9484. *
  9485. * @private
  9486. * @function Highcharts.RangeSelector#alignButtonGroup
  9487. * @param {number} xOffsetForExportButton
  9488. * @param {number} [width]
  9489. * @return {void}
  9490. */
  9491. RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
  9492. var _a = this,
  9493. chart = _a.chart,
  9494. options = _a.options,
  9495. buttonGroup = _a.buttonGroup,
  9496. buttons = _a.buttons;
  9497. var buttonPosition = options.buttonPosition;
  9498. var plotLeft = chart.plotLeft - chart.spacing[3];
  9499. var translateX = buttonPosition.x - chart.spacing[3];
  9500. if (buttonPosition.align === 'right') {
  9501. translateX += xOffsetForExportButton - plotLeft; // #13014
  9502. }
  9503. else if (buttonPosition.align === 'center') {
  9504. translateX -= plotLeft / 2;
  9505. }
  9506. if (buttonGroup) {
  9507. // Align button group
  9508. buttonGroup.align({
  9509. y: buttonPosition.y,
  9510. width: pick(width, this.initialButtonGroupWidth),
  9511. align: buttonPosition.align,
  9512. x: translateX
  9513. }, true, chart.spacingBox);
  9514. }
  9515. };
  9516. /**
  9517. * @private
  9518. * @function Highcharts.RangeSelector#positionButtons
  9519. * @return {void}
  9520. */
  9521. RangeSelector.prototype.positionButtons = function () {
  9522. var _a = this,
  9523. buttons = _a.buttons,
  9524. chart = _a.chart,
  9525. options = _a.options,
  9526. zoomText = _a.zoomText;
  9527. var verb = chart.hasLoaded ? 'animate' : 'attr';
  9528. var buttonPosition = options.buttonPosition;
  9529. var plotLeft = chart.plotLeft;
  9530. var buttonLeft = plotLeft;
  9531. if (zoomText && zoomText.visibility !== 'hidden') {
  9532. // #8769, allow dynamically updating margins
  9533. zoomText[verb]({
  9534. x: pick(plotLeft + buttonPosition.x, plotLeft)
  9535. });
  9536. // Button start position
  9537. buttonLeft += buttonPosition.x +
  9538. zoomText.getBBox().width + 5;
  9539. }
  9540. this.buttonOptions.forEach(function (rangeOptions, i) {
  9541. if (buttons[i].visibility !== 'hidden') {
  9542. buttons[i][verb]({ x: buttonLeft });
  9543. // increase button position for the next button
  9544. buttonLeft += buttons[i].width + options.buttonSpacing;
  9545. }
  9546. else {
  9547. buttons[i][verb]({ x: plotLeft });
  9548. }
  9549. });
  9550. };
  9551. /**
  9552. * Handle collision between the button group and the input group
  9553. *
  9554. * @private
  9555. * @function Highcharts.RangeSelector#handleCollision
  9556. *
  9557. * @param {number} xOffsetForExportButton
  9558. * The X offset of the group required to make room for the
  9559. * exporting button
  9560. * @return {void}
  9561. */
  9562. RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
  9563. var _this = this;
  9564. var _a = this,
  9565. chart = _a.chart,
  9566. buttonGroup = _a.buttonGroup,
  9567. inputGroup = _a.inputGroup;
  9568. var _b = this.options,
  9569. buttonPosition = _b.buttonPosition,
  9570. dropdown = _b.dropdown,
  9571. inputPosition = _b.inputPosition;
  9572. var maxButtonWidth = function () {
  9573. var buttonWidth = 0;
  9574. _this.buttons.forEach(function (button) {
  9575. var bBox = button.getBBox();
  9576. if (bBox.width > buttonWidth) {
  9577. buttonWidth = bBox.width;
  9578. }
  9579. });
  9580. return buttonWidth;
  9581. };
  9582. var groupsOverlap = function (buttonGroupWidth) {
  9583. if (inputGroup && buttonGroup) {
  9584. var inputGroupX = (inputGroup.alignAttr.translateX +
  9585. inputGroup.alignOptions.x -
  9586. xOffsetForExportButton +
  9587. // getBBox for detecing left margin
  9588. inputGroup.getBBox().x +
  9589. // 2px padding to not overlap input and label
  9590. 2);
  9591. var inputGroupWidth = inputGroup.alignOptions.width;
  9592. var buttonGroupX = buttonGroup.alignAttr.translateX +
  9593. buttonGroup.getBBox().x;
  9594. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  9595. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  9596. (buttonPosition.y <
  9597. (inputPosition.y +
  9598. inputGroup.getBBox().height));
  9599. }
  9600. return false;
  9601. };
  9602. var moveInputsDown = function () {
  9603. if (inputGroup && buttonGroup) {
  9604. inputGroup.attr({
  9605. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  9606. 0 :
  9607. -xOffsetForExportButton),
  9608. translateY: inputGroup.alignAttr.translateY +
  9609. buttonGroup.getBBox().height + 10
  9610. });
  9611. }
  9612. };
  9613. if (buttonGroup) {
  9614. if (dropdown === 'always') {
  9615. this.collapseButtons(xOffsetForExportButton);
  9616. if (groupsOverlap(maxButtonWidth())) {
  9617. // Move the inputs down if there is still a collision
  9618. // after collapsing the buttons
  9619. moveInputsDown();
  9620. }
  9621. return;
  9622. }
  9623. if (dropdown === 'never') {
  9624. this.expandButtons();
  9625. }
  9626. }
  9627. // Detect collision
  9628. if (inputGroup && buttonGroup) {
  9629. if ((inputPosition.align === buttonPosition.align) ||
  9630. // 20 is minimal spacing between elements
  9631. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  9632. if (dropdown === 'responsive') {
  9633. this.collapseButtons(xOffsetForExportButton);
  9634. if (groupsOverlap(maxButtonWidth())) {
  9635. moveInputsDown();
  9636. }
  9637. }
  9638. else {
  9639. moveInputsDown();
  9640. }
  9641. }
  9642. else if (dropdown === 'responsive') {
  9643. this.expandButtons();
  9644. }
  9645. }
  9646. else if (buttonGroup && dropdown === 'responsive') {
  9647. if (this.initialButtonGroupWidth > chart.plotWidth) {
  9648. this.collapseButtons(xOffsetForExportButton);
  9649. }
  9650. else {
  9651. this.expandButtons();
  9652. }
  9653. }
  9654. };
  9655. /**
  9656. * Collapse the buttons and put the select element on top.
  9657. *
  9658. * @private
  9659. * @function Highcharts.RangeSelector#collapseButtons
  9660. * @param {number} xOffsetForExportButton
  9661. * @return {void}
  9662. */
  9663. RangeSelector.prototype.collapseButtons = function (xOffsetForExportButton) {
  9664. var _a = this,
  9665. buttons = _a.buttons,
  9666. buttonOptions = _a.buttonOptions,
  9667. dropdown = _a.dropdown,
  9668. options = _a.options,
  9669. zoomText = _a.zoomText;
  9670. var getAttribs = function (text) { return ({
  9671. text: text ? text + " \u25BE" : '▾',
  9672. width: 'auto',
  9673. paddingLeft: 8,
  9674. paddingRight: 8
  9675. }); };
  9676. if (zoomText) {
  9677. zoomText.hide();
  9678. }
  9679. var hasActiveButton = false;
  9680. buttonOptions.forEach(function (rangeOptions, i) {
  9681. var button = buttons[i];
  9682. if (button.state !== 2) {
  9683. button.hide();
  9684. }
  9685. else {
  9686. button.show();
  9687. button.attr(getAttribs(rangeOptions.text));
  9688. hasActiveButton = true;
  9689. }
  9690. });
  9691. if (!hasActiveButton) {
  9692. if (dropdown) {
  9693. dropdown.selectedIndex = 0;
  9694. }
  9695. buttons[0].show();
  9696. buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));
  9697. }
  9698. var align = options.buttonPosition.align;
  9699. this.positionButtons();
  9700. if (align === 'right' || align === 'center') {
  9701. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  9702. }
  9703. this.showDropdown();
  9704. };
  9705. /**
  9706. * Show all the buttons and hide the select element.
  9707. *
  9708. * @private
  9709. * @function Highcharts.RangeSelector#expandButtons
  9710. * @return {void}
  9711. */
  9712. RangeSelector.prototype.expandButtons = function () {
  9713. var _a = this,
  9714. buttons = _a.buttons,
  9715. buttonOptions = _a.buttonOptions,
  9716. options = _a.options,
  9717. zoomText = _a.zoomText;
  9718. this.hideDropdown();
  9719. if (zoomText) {
  9720. zoomText.show();
  9721. }
  9722. buttonOptions.forEach(function (rangeOptions, i) {
  9723. var button = buttons[i];
  9724. button.show();
  9725. button.attr({
  9726. text: rangeOptions.text,
  9727. width: options.buttonTheme.width || 28,
  9728. paddingLeft: 'unset',
  9729. paddingRight: 'unset'
  9730. });
  9731. if (button.state < 2) {
  9732. button.setState(0);
  9733. }
  9734. });
  9735. this.positionButtons();
  9736. };
  9737. /**
  9738. * Get the index of the visible button when the buttons are collapsed.
  9739. *
  9740. * @private
  9741. * @function Highcharts.RangeSelector#currentButtonIndex
  9742. * @return {number}
  9743. */
  9744. RangeSelector.prototype.currentButtonIndex = function () {
  9745. var dropdown = this.dropdown;
  9746. if (dropdown && dropdown.selectedIndex > 0) {
  9747. return dropdown.selectedIndex - 1;
  9748. }
  9749. return 0;
  9750. };
  9751. /**
  9752. * Position the select element on top of the button.
  9753. *
  9754. * @private
  9755. * @function Highcharts.RangeSelector#showDropdown
  9756. * @return {void}
  9757. */
  9758. RangeSelector.prototype.showDropdown = function () {
  9759. var _a = this,
  9760. buttonGroup = _a.buttonGroup,
  9761. buttons = _a.buttons,
  9762. chart = _a.chart,
  9763. dropdown = _a.dropdown;
  9764. if (buttonGroup && dropdown) {
  9765. var translateX = buttonGroup.translateX,
  9766. translateY = buttonGroup.translateY;
  9767. var bBox = buttons[this.currentButtonIndex()].getBBox();
  9768. css(dropdown, {
  9769. left: (chart.plotLeft + translateX) + 'px',
  9770. top: (translateY + 0.5) + 'px',
  9771. width: bBox.width + 'px',
  9772. height: bBox.height + 'px'
  9773. });
  9774. this.hasVisibleDropdown = true;
  9775. }
  9776. };
  9777. /**
  9778. * @private
  9779. * @function Highcharts.RangeSelector#hideDropdown
  9780. * @return {void}
  9781. */
  9782. RangeSelector.prototype.hideDropdown = function () {
  9783. var dropdown = this.dropdown;
  9784. if (dropdown) {
  9785. css(dropdown, {
  9786. top: '-9999em',
  9787. width: '1px',
  9788. height: '1px'
  9789. });
  9790. this.hasVisibleDropdown = false;
  9791. }
  9792. };
  9793. /**
  9794. * Extracts height of range selector
  9795. *
  9796. * @private
  9797. * @function Highcharts.RangeSelector#getHeight
  9798. * @return {number}
  9799. * Returns rangeSelector height
  9800. */
  9801. RangeSelector.prototype.getHeight = function () {
  9802. var rangeSelector = this,
  9803. options = rangeSelector.options,
  9804. rangeSelectorGroup = rangeSelector.group,
  9805. inputPosition = options.inputPosition,
  9806. buttonPosition = options.buttonPosition,
  9807. yPosition = options.y,
  9808. buttonPositionY = buttonPosition.y,
  9809. inputPositionY = inputPosition.y,
  9810. rangeSelectorHeight = 0,
  9811. minPosition;
  9812. if (options.height) {
  9813. return options.height;
  9814. }
  9815. // Align the elements before we read the height in case we're switching
  9816. // between wrapped and non-wrapped layout
  9817. this.alignElements();
  9818. rangeSelectorHeight = rangeSelectorGroup ?
  9819. // 13px to keep back compatibility
  9820. (rangeSelectorGroup.getBBox(true).height) + 13 +
  9821. yPosition :
  9822. 0;
  9823. minPosition = Math.min(inputPositionY, buttonPositionY);
  9824. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  9825. (inputPositionY > 0 && buttonPositionY > 0)) {
  9826. rangeSelectorHeight += Math.abs(minPosition);
  9827. }
  9828. return rangeSelectorHeight;
  9829. };
  9830. /**
  9831. * Detect collision with title or subtitle
  9832. *
  9833. * @private
  9834. * @function Highcharts.RangeSelector#titleCollision
  9835. *
  9836. * @param {Highcharts.Chart} chart
  9837. *
  9838. * @return {boolean}
  9839. * Returns collision status
  9840. */
  9841. RangeSelector.prototype.titleCollision = function (chart) {
  9842. return !(chart.options.title.text ||
  9843. chart.options.subtitle.text);
  9844. };
  9845. /**
  9846. * Update the range selector with new options
  9847. *
  9848. * @private
  9849. * @function Highcharts.RangeSelector#update
  9850. * @param {Highcharts.RangeSelectorOptions} options
  9851. * @return {void}
  9852. */
  9853. RangeSelector.prototype.update = function (options) {
  9854. var chart = this.chart;
  9855. merge(true, chart.options.rangeSelector, options);
  9856. this.destroy();
  9857. this.init(chart);
  9858. this.render();
  9859. };
  9860. /**
  9861. * Destroys allocated elements.
  9862. *
  9863. * @private
  9864. * @function Highcharts.RangeSelector#destroy
  9865. */
  9866. RangeSelector.prototype.destroy = function () {
  9867. var rSelector = this,
  9868. minInput = rSelector.minInput,
  9869. maxInput = rSelector.maxInput;
  9870. if (rSelector.eventsToUnbind) {
  9871. rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
  9872. rSelector.eventsToUnbind = void 0;
  9873. }
  9874. // Destroy elements in collections
  9875. destroyObjectProperties(rSelector.buttons);
  9876. // Clear input element events
  9877. if (minInput) {
  9878. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  9879. }
  9880. if (maxInput) {
  9881. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  9882. }
  9883. // Destroy HTML and SVG elements
  9884. objectEach(rSelector, function (val, key) {
  9885. if (val && key !== 'chart') {
  9886. if (val instanceof SVGElement) {
  9887. // SVGElement
  9888. val.destroy();
  9889. }
  9890. else if (val instanceof window.HTMLElement) {
  9891. // HTML element
  9892. discardElement(val);
  9893. }
  9894. }
  9895. if (val !== RangeSelector.prototype[key]) {
  9896. rSelector[key] = null;
  9897. }
  9898. }, this);
  9899. };
  9900. return RangeSelector;
  9901. }());
  9902. /**
  9903. * The default buttons for pre-selecting time frames
  9904. */
  9905. RangeSelector.prototype.defaultButtons = [{
  9906. type: 'month',
  9907. count: 1,
  9908. text: '1m',
  9909. title: 'View 1 month'
  9910. }, {
  9911. type: 'month',
  9912. count: 3,
  9913. text: '3m',
  9914. title: 'View 3 months'
  9915. }, {
  9916. type: 'month',
  9917. count: 6,
  9918. text: '6m',
  9919. title: 'View 6 months'
  9920. }, {
  9921. type: 'ytd',
  9922. text: 'YTD',
  9923. title: 'View year to date'
  9924. }, {
  9925. type: 'year',
  9926. count: 1,
  9927. text: '1y',
  9928. title: 'View 1 year'
  9929. }, {
  9930. type: 'all',
  9931. text: 'All',
  9932. title: 'View all'
  9933. }];
  9934. /**
  9935. * The date formats to use when setting min, max and value on date inputs
  9936. */
  9937. RangeSelector.prototype.inputTypeFormats = {
  9938. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  9939. 'date': '%Y-%m-%d',
  9940. 'time': '%H:%M:%S'
  9941. };
  9942. /**
  9943. * Get the preferred input type based on a date format string.
  9944. *
  9945. * @private
  9946. * @function preferredInputType
  9947. * @param {string} format
  9948. * @return {string}
  9949. */
  9950. function preferredInputType(format) {
  9951. var ms = format.indexOf('%L') !== -1;
  9952. if (ms) {
  9953. return 'text';
  9954. }
  9955. var date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y'].some(function (char) {
  9956. return format.indexOf('%' + char) !== -1;
  9957. });
  9958. var time = ['H', 'k', 'I', 'l', 'M', 'S'].some(function (char) {
  9959. return format.indexOf('%' + char) !== -1;
  9960. });
  9961. if (date && time) {
  9962. return 'datetime-local';
  9963. }
  9964. if (date) {
  9965. return 'date';
  9966. }
  9967. if (time) {
  9968. return 'time';
  9969. }
  9970. return 'text';
  9971. }
  9972. /**
  9973. * Get the axis min value based on the range option and the current max. For
  9974. * stock charts this is extended via the {@link RangeSelector} so that if the
  9975. * selected range is a multiple of months or years, it is compensated for
  9976. * various month lengths.
  9977. *
  9978. * @private
  9979. * @function Highcharts.Axis#minFromRange
  9980. * @return {number|undefined}
  9981. * The new minimum value.
  9982. */
  9983. Axis.prototype.minFromRange = function () {
  9984. var rangeOptions = this.range,
  9985. type = rangeOptions.type,
  9986. min,
  9987. max = this.max,
  9988. dataMin,
  9989. range,
  9990. time = this.chart.time,
  9991. // Get the true range from a start date
  9992. getTrueRange = function (base,
  9993. count) {
  9994. var timeName = type === 'year' ? 'FullYear' : 'Month';
  9995. var date = new time.Date(base);
  9996. var basePeriod = time.get(timeName,
  9997. date);
  9998. time.set(timeName, date, basePeriod + count);
  9999. if (basePeriod === time.get(timeName, date)) {
  10000. time.set('Date', date, 0); // #6537
  10001. }
  10002. return date.getTime() - base;
  10003. };
  10004. if (isNumber(rangeOptions)) {
  10005. min = max - rangeOptions;
  10006. range = rangeOptions;
  10007. }
  10008. else {
  10009. min = max + getTrueRange(max, -rangeOptions.count);
  10010. // Let the fixedRange reflect initial settings (#5930)
  10011. if (this.chart) {
  10012. this.chart.fixedRange = max - min;
  10013. }
  10014. }
  10015. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  10016. if (!isNumber(min)) {
  10017. min = dataMin;
  10018. }
  10019. if (min <= dataMin) {
  10020. min = dataMin;
  10021. if (typeof range === 'undefined') { // #4501
  10022. range = getTrueRange(min, rangeOptions.count);
  10023. }
  10024. this.newMax = Math.min(min + range, this.dataMax);
  10025. }
  10026. if (!isNumber(max)) {
  10027. min = void 0;
  10028. }
  10029. return min;
  10030. };
  10031. if (!H.RangeSelector) {
  10032. var chartDestroyEvents_1 = [];
  10033. var initRangeSelector_1 = function (chart) {
  10034. var extremes,
  10035. rangeSelector = chart.rangeSelector,
  10036. legend,
  10037. alignTo,
  10038. verticalAlign;
  10039. /**
  10040. * @private
  10041. */
  10042. function render() {
  10043. if (rangeSelector) {
  10044. extremes = chart.xAxis[0].getExtremes();
  10045. legend = chart.legend;
  10046. verticalAlign = (rangeSelector &&
  10047. rangeSelector.options.verticalAlign);
  10048. if (isNumber(extremes.min)) {
  10049. rangeSelector.render(extremes.min, extremes.max);
  10050. }
  10051. // Re-align the legend so that it's below the rangeselector
  10052. if (legend.display &&
  10053. verticalAlign === 'top' &&
  10054. verticalAlign === legend.options.verticalAlign) {
  10055. // Create a new alignment box for the legend.
  10056. alignTo = merge(chart.spacingBox);
  10057. if (legend.options.layout === 'vertical') {
  10058. alignTo.y = chart.plotTop;
  10059. }
  10060. else {
  10061. alignTo.y += rangeSelector.getHeight();
  10062. }
  10063. legend.group.placed = false; // Don't animate the alignment.
  10064. legend.align(alignTo);
  10065. }
  10066. }
  10067. }
  10068. if (rangeSelector) {
  10069. var events = find(chartDestroyEvents_1,
  10070. function (e) { return e[0] === chart; });
  10071. if (!events) {
  10072. chartDestroyEvents_1.push([chart, [
  10073. // redraw the scroller on setExtremes
  10074. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  10075. if (rangeSelector) {
  10076. rangeSelector.render(e.min, e.max);
  10077. }
  10078. }),
  10079. // redraw the scroller chart resize
  10080. addEvent(chart, 'redraw', render)
  10081. ]]);
  10082. }
  10083. // do it now
  10084. render();
  10085. }
  10086. };
  10087. // Initialize rangeselector for stock charts
  10088. addEvent(Chart, 'afterGetContainer', function () {
  10089. if (this.options.rangeSelector &&
  10090. this.options.rangeSelector.enabled) {
  10091. this.rangeSelector = new RangeSelector(this);
  10092. }
  10093. });
  10094. addEvent(Chart, 'beforeRender', function () {
  10095. var chart = this,
  10096. axes = chart.axes,
  10097. rangeSelector = chart.rangeSelector,
  10098. verticalAlign;
  10099. if (rangeSelector) {
  10100. if (isNumber(rangeSelector.deferredYTDClick)) {
  10101. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  10102. delete rangeSelector.deferredYTDClick;
  10103. }
  10104. axes.forEach(function (axis) {
  10105. axis.updateNames();
  10106. axis.setScale();
  10107. });
  10108. chart.getAxisMargins();
  10109. rangeSelector.render();
  10110. verticalAlign = rangeSelector.options.verticalAlign;
  10111. if (!rangeSelector.options.floating) {
  10112. if (verticalAlign === 'bottom') {
  10113. this.extraBottomMargin = true;
  10114. }
  10115. else if (verticalAlign !== 'middle') {
  10116. this.extraTopMargin = true;
  10117. }
  10118. }
  10119. }
  10120. });
  10121. addEvent(Chart, 'update', function (e) {
  10122. var chart = this,
  10123. options = e.options,
  10124. optionsRangeSelector = options.rangeSelector,
  10125. rangeSelector = chart.rangeSelector,
  10126. verticalAlign,
  10127. extraBottomMarginWas = this.extraBottomMargin,
  10128. extraTopMarginWas = this.extraTopMargin;
  10129. if (optionsRangeSelector &&
  10130. optionsRangeSelector.enabled &&
  10131. !defined(rangeSelector) &&
  10132. this.options.rangeSelector) {
  10133. this.options.rangeSelector.enabled = true;
  10134. this.rangeSelector = rangeSelector = new RangeSelector(this);
  10135. }
  10136. this.extraBottomMargin = false;
  10137. this.extraTopMargin = false;
  10138. if (rangeSelector) {
  10139. initRangeSelector_1(this);
  10140. verticalAlign = (optionsRangeSelector &&
  10141. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  10142. if (!rangeSelector.options.floating) {
  10143. if (verticalAlign === 'bottom') {
  10144. this.extraBottomMargin = true;
  10145. }
  10146. else if (verticalAlign !== 'middle') {
  10147. this.extraTopMargin = true;
  10148. }
  10149. }
  10150. if (this.extraBottomMargin !== extraBottomMarginWas ||
  10151. this.extraTopMargin !== extraTopMarginWas) {
  10152. this.isDirtyBox = true;
  10153. }
  10154. }
  10155. });
  10156. addEvent(Chart, 'render', function () {
  10157. var chart = this,
  10158. rangeSelector = chart.rangeSelector,
  10159. verticalAlign;
  10160. if (rangeSelector && !rangeSelector.options.floating) {
  10161. rangeSelector.render();
  10162. verticalAlign = rangeSelector.options.verticalAlign;
  10163. if (verticalAlign === 'bottom') {
  10164. this.extraBottomMargin = true;
  10165. }
  10166. else if (verticalAlign !== 'middle') {
  10167. this.extraTopMargin = true;
  10168. }
  10169. }
  10170. });
  10171. addEvent(Chart, 'getMargins', function () {
  10172. var rangeSelector = this.rangeSelector,
  10173. rangeSelectorHeight;
  10174. if (rangeSelector) {
  10175. rangeSelectorHeight = rangeSelector.getHeight();
  10176. if (this.extraTopMargin) {
  10177. this.plotTop += rangeSelectorHeight;
  10178. }
  10179. if (this.extraBottomMargin) {
  10180. this.marginBottom += rangeSelectorHeight;
  10181. }
  10182. }
  10183. });
  10184. Chart.prototype.callbacks.push(initRangeSelector_1);
  10185. // Remove resize/afterSetExtremes at chart destroy
  10186. addEvent(Chart, 'destroy', function destroyEvents() {
  10187. for (var i = 0; i < chartDestroyEvents_1.length; i++) {
  10188. var events = chartDestroyEvents_1[i];
  10189. if (events[0] === this) {
  10190. events[1].forEach(function (unbind) { return unbind(); });
  10191. chartDestroyEvents_1.splice(i, 1);
  10192. return;
  10193. }
  10194. }
  10195. });
  10196. H.RangeSelector = RangeSelector;
  10197. }
  10198. return H.RangeSelector;
  10199. });
  10200. _registerModule(_modules, 'Core/Chart/StockChart.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/FormatUtilities.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (A, Axis, Chart, F, O, palette, Point, Series, SVGRenderer, U) {
  10201. /* *
  10202. *
  10203. * (c) 2010-2021 Torstein Honsi
  10204. *
  10205. * License: www.highcharts.com/license
  10206. *
  10207. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10208. *
  10209. * */
  10210. var __extends = (this && this.__extends) || (function () {
  10211. var extendStatics = function (d,
  10212. b) {
  10213. extendStatics = Object.setPrototypeOf ||
  10214. ({ __proto__: [] } instanceof Array && function (d,
  10215. b) { d.__proto__ = b; }) ||
  10216. function (d,
  10217. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  10218. return extendStatics(d, b);
  10219. };
  10220. return function (d, b) {
  10221. extendStatics(d, b);
  10222. function __() { this.constructor = d; }
  10223. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  10224. };
  10225. })();
  10226. var animObject = A.animObject;
  10227. var format = F.format;
  10228. var getOptions = O.getOptions;
  10229. var pointTooltipFormatter = Point.prototype.tooltipFormatter;
  10230. var _a = Series.prototype,
  10231. seriesInit = _a.init,
  10232. seriesProcessData = _a.processData;
  10233. var addEvent = U.addEvent,
  10234. arrayMax = U.arrayMax,
  10235. arrayMin = U.arrayMin,
  10236. clamp = U.clamp,
  10237. defined = U.defined,
  10238. extend = U.extend,
  10239. find = U.find,
  10240. isNumber = U.isNumber,
  10241. isString = U.isString,
  10242. merge = U.merge,
  10243. pick = U.pick,
  10244. splat = U.splat;
  10245. // Has a dependency on Navigator due to the use of
  10246. // defaultOptions.navigator
  10247. // Has a dependency on Scrollbar due to the use of
  10248. // defaultOptions.scrollbar
  10249. // Has a dependency on RangeSelector due to the use of
  10250. // defaultOptions.rangeSelector
  10251. /**
  10252. * Stock-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
  10253. *
  10254. * @requires modules/stock
  10255. *
  10256. * @class
  10257. * @name Highcharts.StockChart
  10258. * @extends Highcharts.Chart
  10259. */
  10260. var StockChart = /** @class */ (function (_super) {
  10261. __extends(StockChart, _super);
  10262. function StockChart() {
  10263. return _super !== null && _super.apply(this, arguments) || this;
  10264. }
  10265. /**
  10266. * Initializes the chart. The constructor's arguments are passed on
  10267. * directly.
  10268. *
  10269. * @function Highcharts.StockChart#init
  10270. *
  10271. * @param {Highcharts.Options} userOptions
  10272. * Custom options.
  10273. *
  10274. * @param {Function} [callback]
  10275. * Function to run when the chart has loaded and and all external
  10276. * images are loaded.
  10277. *
  10278. * @return {void}
  10279. *
  10280. * @fires Highcharts.StockChart#event:init
  10281. * @fires Highcharts.StockChart#event:afterInit
  10282. */
  10283. StockChart.prototype.init = function (userOptions, callback) {
  10284. var defaultOptions = getOptions(),
  10285. xAxisOptions = userOptions.xAxis,
  10286. yAxisOptions = userOptions.yAxis,
  10287. // Always disable startOnTick:true on the main axis when the
  10288. // navigator is enabled (#1090)
  10289. navigatorEnabled = pick(userOptions.navigator && userOptions.navigator.enabled,
  10290. defaultOptions.navigator.enabled,
  10291. true);
  10292. // Avoid doing these twice
  10293. userOptions.xAxis = userOptions.yAxis = void 0;
  10294. var options = merge({
  10295. chart: {
  10296. panning: {
  10297. enabled: true,
  10298. type: 'x'
  10299. },
  10300. pinchType: 'x'
  10301. },
  10302. navigator: {
  10303. enabled: navigatorEnabled
  10304. },
  10305. scrollbar: {
  10306. // #4988 - check if setOptions was called
  10307. enabled: pick(defaultOptions.scrollbar.enabled,
  10308. true)
  10309. },
  10310. rangeSelector: {
  10311. // #4988 - check if setOptions was called
  10312. enabled: pick(defaultOptions.rangeSelector.enabled,
  10313. true)
  10314. },
  10315. title: {
  10316. text: null
  10317. },
  10318. tooltip: {
  10319. split: pick(defaultOptions.tooltip.split,
  10320. true),
  10321. crosshairs: true
  10322. },
  10323. legend: {
  10324. enabled: false
  10325. }
  10326. },
  10327. userOptions, // user's options
  10328. {
  10329. isStock: true // internal flag
  10330. });
  10331. userOptions.xAxis = xAxisOptions;
  10332. userOptions.yAxis = yAxisOptions;
  10333. // apply X axis options to both single and multi y axes
  10334. options.xAxis = splat(userOptions.xAxis || {}).map(function (xAxisOptions, i) {
  10335. return merge(getDefaultAxisOptions('xAxis', xAxisOptions), defaultOptions.xAxis, // #3802
  10336. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  10337. xAxisOptions, // user options
  10338. getForcedAxisOptions('xAxis', userOptions));
  10339. });
  10340. // apply Y axis options to both single and multi y axes
  10341. options.yAxis = splat(userOptions.yAxis || {}).map(function (yAxisOptions, i) {
  10342. return merge(getDefaultAxisOptions('yAxis', yAxisOptions), defaultOptions.yAxis, // #3802
  10343. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  10344. yAxisOptions // user options
  10345. );
  10346. });
  10347. _super.prototype.init.call(this, options, callback);
  10348. };
  10349. /**
  10350. * Factory for creating different axis types.
  10351. * Extended to add stock defaults.
  10352. *
  10353. * @private
  10354. * @function Highcharts.StockChart#createAxis
  10355. *
  10356. * @param {string} type
  10357. * An axis type.
  10358. *
  10359. * @param {Chart.CreateAxisOptionsObject} options
  10360. * The axis creation options.
  10361. *
  10362. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  10363. */
  10364. StockChart.prototype.createAxis = function (type, options) {
  10365. options.axis = merge(getDefaultAxisOptions(type, options.axis), options.axis, getForcedAxisOptions(type, this.userOptions));
  10366. return _super.prototype.createAxis.call(this, type, options);
  10367. };
  10368. return StockChart;
  10369. }(Chart));
  10370. /* eslint-disable no-invalid-this, valid-jsdoc */
  10371. (function (StockChart) {
  10372. /**
  10373. * Factory function for creating new stock charts. Creates a new
  10374. * {@link Highcharts.StockChart|StockChart} object with different default
  10375. * options than the basic Chart.
  10376. *
  10377. * @example
  10378. * let chart = Highcharts.stockChart('container', {
  10379. * series: [{
  10380. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  10381. * pointInterval: 24 * 60 * 60 * 1000
  10382. * }]
  10383. * });
  10384. *
  10385. * @function Highcharts.stockChart
  10386. *
  10387. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  10388. * The DOM element to render to, or its id.
  10389. *
  10390. * @param {Highcharts.Options} options
  10391. * The chart options structure as described in the
  10392. * [options reference](https://api.highcharts.com/highstock).
  10393. *
  10394. * @param {Highcharts.ChartCallbackFunction} [callback]
  10395. * A function to execute when the chart object is finished loading
  10396. * and rendering. In most cases the chart is built in one thread,
  10397. * but in Internet Explorer version 8 or less the chart is sometimes
  10398. * initialized before the document is ready, and in these cases the
  10399. * chart object will not be finished synchronously. As a
  10400. * consequence, code that relies on the newly built Chart object
  10401. * should always run in the callback. Defining a
  10402. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  10403. * handler is equivalent.
  10404. *
  10405. * @return {Highcharts.StockChart}
  10406. * The chart object.
  10407. */
  10408. function stockChart(a, b, c) {
  10409. return new StockChart(a, b, c);
  10410. }
  10411. StockChart.stockChart = stockChart;
  10412. })(StockChart || (StockChart = {}));
  10413. /**
  10414. * Get stock-specific default axis options.
  10415. *
  10416. * @private
  10417. * @function getDefaultAxisOptions
  10418. * @param {string} type
  10419. * @param {Highcharts.AxisOptions} options
  10420. * @return {Highcharts.AxisOptions}
  10421. */
  10422. function getDefaultAxisOptions(type, options) {
  10423. if (type === 'xAxis') {
  10424. return {
  10425. minPadding: 0,
  10426. maxPadding: 0,
  10427. overscroll: 0,
  10428. ordinal: true,
  10429. title: {
  10430. text: null
  10431. },
  10432. labels: {
  10433. overflow: 'justify'
  10434. },
  10435. showLastLabel: true
  10436. };
  10437. }
  10438. if (type === 'yAxis') {
  10439. return {
  10440. labels: {
  10441. y: -2
  10442. },
  10443. opposite: pick(options.opposite, true),
  10444. /**
  10445. * @default {highcharts} true
  10446. * @default {highstock} false
  10447. * @apioption yAxis.showLastLabel
  10448. *
  10449. * @private
  10450. */
  10451. showLastLabel: !!(
  10452. // #6104, show last label by default for category axes
  10453. options.categories ||
  10454. options.type === 'category'),
  10455. title: {
  10456. text: null
  10457. }
  10458. };
  10459. }
  10460. return {};
  10461. }
  10462. /**
  10463. * Get stock-specific forced axis options.
  10464. *
  10465. * @private
  10466. * @function getForcedAxisOptions
  10467. * @param {string} type
  10468. * @param {Highcharts.Options} chartOptions
  10469. * @return {Highcharts.AxisOptions}
  10470. */
  10471. function getForcedAxisOptions(type, chartOptions) {
  10472. if (type === 'xAxis') {
  10473. var defaultOptions = getOptions(),
  10474. // Always disable startOnTick:true on the main axis when the
  10475. // navigator is enabled (#1090)
  10476. navigatorEnabled = pick(chartOptions.navigator && chartOptions.navigator.enabled,
  10477. defaultOptions.navigator.enabled,
  10478. true);
  10479. var axisOptions = {
  10480. type: 'datetime',
  10481. categories: void 0
  10482. };
  10483. if (navigatorEnabled) {
  10484. axisOptions.startOnTick = false;
  10485. axisOptions.endOnTick = false;
  10486. }
  10487. return axisOptions;
  10488. }
  10489. return {};
  10490. }
  10491. /* *
  10492. *
  10493. * Compositions
  10494. *
  10495. * */
  10496. // Handle som Stock-specific series defaults, override the plotOptions before
  10497. // series options are handled.
  10498. addEvent(Series, 'setOptions', function (e) {
  10499. var overrides;
  10500. if (this.chart.options.isStock) {
  10501. if (this.is('column') || this.is('columnrange')) {
  10502. overrides = {
  10503. borderWidth: 0,
  10504. shadow: false
  10505. };
  10506. }
  10507. else if (!this.is('scatter') && !this.is('sma')) {
  10508. overrides = {
  10509. marker: {
  10510. enabled: false,
  10511. radius: 2
  10512. }
  10513. };
  10514. }
  10515. if (overrides) {
  10516. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  10517. }
  10518. }
  10519. });
  10520. // Override the automatic label alignment so that the first Y axis' labels
  10521. // are drawn on top of the grid line, and subsequent axes are drawn outside
  10522. addEvent(Axis, 'autoLabelAlign', function (e) {
  10523. var chart = this.chart,
  10524. options = this.options,
  10525. panes = chart._labelPanes = chart._labelPanes || {},
  10526. key,
  10527. labelOptions = this.options.labels;
  10528. if (this.chart.options.isStock && this.coll === 'yAxis') {
  10529. key = options.top + ',' + options.height;
  10530. // do it only for the first Y axis of each pane
  10531. if (!panes[key] && labelOptions.enabled) {
  10532. if (labelOptions.x === 15) { // default
  10533. labelOptions.x = 0;
  10534. }
  10535. if (typeof labelOptions.align === 'undefined') {
  10536. labelOptions.align = 'right';
  10537. }
  10538. panes[key] = this;
  10539. e.align = 'right';
  10540. e.preventDefault();
  10541. }
  10542. }
  10543. });
  10544. // Clear axis from label panes (#6071)
  10545. addEvent(Axis, 'destroy', function () {
  10546. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  10547. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  10548. delete chart._labelPanes[key];
  10549. }
  10550. });
  10551. // Override getPlotLinePath to allow for multipane charts
  10552. addEvent(Axis, 'getPlotLinePath', function (e) {
  10553. var axis = this,
  10554. series = (this.isLinked && !this.series ?
  10555. this.linkedParent.series :
  10556. this.series),
  10557. chart = axis.chart,
  10558. renderer = chart.renderer,
  10559. axisLeft = axis.left,
  10560. axisTop = axis.top,
  10561. x1,
  10562. y1,
  10563. x2,
  10564. y2,
  10565. result = [],
  10566. axes = [], // #3416 need a default array
  10567. axes2,
  10568. uniqueAxes,
  10569. translatedValue = e.translatedValue,
  10570. value = e.value,
  10571. force = e.force,
  10572. transVal;
  10573. /**
  10574. * Return the other axis based on either the axis option or on related
  10575. * series.
  10576. * @private
  10577. */
  10578. function getAxis(coll) {
  10579. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis',
  10580. opt = axis.options[otherColl];
  10581. // Other axis indexed by number
  10582. if (isNumber(opt)) {
  10583. return [chart[otherColl][opt]];
  10584. }
  10585. // Other axis indexed by id (like navigator)
  10586. if (isString(opt)) {
  10587. return [chart.get(opt)];
  10588. }
  10589. // Auto detect based on existing series
  10590. return series.map(function (s) {
  10591. return s[otherColl];
  10592. });
  10593. }
  10594. if ( // For stock chart, by default render paths across the panes
  10595. // except the case when `acrossPanes` is disabled by user (#6644)
  10596. (chart.options.isStock && e.acrossPanes !== false) &&
  10597. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  10598. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  10599. e.preventDefault();
  10600. // Get the related axes based on series
  10601. axes = getAxis(axis.coll);
  10602. // Get the related axes based options.*Axis setting #2810
  10603. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  10604. axes2.forEach(function (A) {
  10605. if (defined(A.options.id) ?
  10606. A.options.id.indexOf('navigator') === -1 :
  10607. true) {
  10608. var a = (A.isXAxis ? 'yAxis' : 'xAxis'),
  10609. rax = (defined(A.options[a]) ?
  10610. chart[a][A.options[a]] :
  10611. chart[a][0]);
  10612. if (axis === rax) {
  10613. axes.push(A);
  10614. }
  10615. }
  10616. });
  10617. // Remove duplicates in the axes array. If there are no axes in the axes
  10618. // array, we are adding an axis without data, so we need to populate
  10619. // this with grid lines (#2796).
  10620. uniqueAxes = axes.length ?
  10621. [] :
  10622. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  10623. axes.forEach(function (axis2) {
  10624. if (uniqueAxes.indexOf(axis2) === -1 &&
  10625. // Do not draw on axis which overlap completely. #5424
  10626. !find(uniqueAxes, function (unique) {
  10627. return unique.pos === axis2.pos && unique.len === axis2.len;
  10628. })) {
  10629. uniqueAxes.push(axis2);
  10630. }
  10631. });
  10632. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  10633. if (isNumber(transVal)) {
  10634. if (axis.horiz) {
  10635. uniqueAxes.forEach(function (axis2) {
  10636. var skip;
  10637. y1 = axis2.pos;
  10638. y2 = y1 + axis2.len;
  10639. x1 = x2 = Math.round(transVal + axis.transB);
  10640. // outside plot area
  10641. if (force !== 'pass' &&
  10642. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  10643. if (force) {
  10644. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  10645. }
  10646. else {
  10647. skip = true;
  10648. }
  10649. }
  10650. if (!skip) {
  10651. result.push(['M', x1, y1], ['L', x2, y2]);
  10652. }
  10653. });
  10654. }
  10655. else {
  10656. uniqueAxes.forEach(function (axis2) {
  10657. var skip;
  10658. x1 = axis2.pos;
  10659. x2 = x1 + axis2.len;
  10660. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  10661. // outside plot area
  10662. if (force !== 'pass' &&
  10663. (y1 < axisTop || y1 > axisTop + axis.height)) {
  10664. if (force) {
  10665. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  10666. }
  10667. else {
  10668. skip = true;
  10669. }
  10670. }
  10671. if (!skip) {
  10672. result.push(['M', x1, y1], ['L', x2, y2]);
  10673. }
  10674. });
  10675. }
  10676. }
  10677. e.path = result.length > 0 ?
  10678. renderer.crispPolyLine(result, e.lineWidth || 1) :
  10679. // #3557 getPlotLinePath in regular Highcharts also returns null
  10680. null;
  10681. }
  10682. });
  10683. /**
  10684. * Function to crisp a line with multiple segments
  10685. *
  10686. * @private
  10687. * @function Highcharts.SVGRenderer#crispPolyLine
  10688. * @param {Highcharts.SVGPathArray} points
  10689. * @param {number} width
  10690. * @return {Highcharts.SVGPathArray}
  10691. */
  10692. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  10693. // points format: [['M', 0, 0], ['L', 100, 0]]
  10694. // normalize to a crisp line
  10695. for (var i = 0; i < points.length; i = i + 2) {
  10696. var start = points[i],
  10697. end = points[i + 1];
  10698. if (start[1] === end[1]) {
  10699. // Substract due to #1129. Now bottom and left axis gridlines behave
  10700. // the same.
  10701. start[1] = end[1] =
  10702. Math.round(start[1]) - (width % 2 / 2);
  10703. }
  10704. if (start[2] === end[2]) {
  10705. start[2] = end[2] =
  10706. Math.round(start[2]) + (width % 2 / 2);
  10707. }
  10708. }
  10709. return points;
  10710. };
  10711. // Wrapper to hide the label
  10712. addEvent(Axis, 'afterHideCrosshair', function () {
  10713. if (this.crossLabel) {
  10714. this.crossLabel = this.crossLabel.hide();
  10715. }
  10716. });
  10717. // Extend crosshairs to also draw the label
  10718. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  10719. // Check if the label has to be drawn
  10720. if (!this.crosshair ||
  10721. !this.crosshair.label ||
  10722. !this.crosshair.label.enabled ||
  10723. !this.cross ||
  10724. !isNumber(this.min) ||
  10725. !isNumber(this.max)) {
  10726. return;
  10727. }
  10728. var chart = this.chart, log = this.logarithmic, options = this.crosshair.label, // the label's options
  10729. horiz = this.horiz, // axis orientation
  10730. opposite = this.opposite, // axis position
  10731. left = this.left, // left position
  10732. top = this.top, // top position
  10733. crossLabel = this.crossLabel, // the svgElement
  10734. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, offset = 0,
  10735. // Use last available event (#5287)
  10736. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  10737. if (log) {
  10738. min = log.lin2log(min);
  10739. max = log.lin2log(max);
  10740. }
  10741. align = (horiz ? 'center' : opposite ?
  10742. (this.labelAlign === 'right' ? 'right' : 'left') :
  10743. (this.labelAlign === 'left' ? 'left' : 'center'));
  10744. // If the label does not exist yet, create it.
  10745. if (!crossLabel) {
  10746. crossLabel = this.crossLabel = chart.renderer
  10747. .label('', 0, void 0, options.shape || 'callout')
  10748. .addClass('highcharts-crosshair-label' + (this.series[0] &&
  10749. ' highcharts-color-' + this.series[0].colorIndex))
  10750. .attr({
  10751. align: options.align || align,
  10752. padding: pick(options.padding, 8),
  10753. r: pick(options.borderRadius, 3),
  10754. zIndex: 2
  10755. })
  10756. .add(this.labelGroup);
  10757. // Presentational
  10758. if (!chart.styledMode) {
  10759. crossLabel
  10760. .attr({
  10761. fill: options.backgroundColor ||
  10762. point && point.series && point.series.color || // #14888
  10763. palette.neutralColor60,
  10764. stroke: options.borderColor || '',
  10765. 'stroke-width': options.borderWidth || 0
  10766. })
  10767. .css(extend({
  10768. color: palette.backgroundColor,
  10769. fontWeight: 'normal',
  10770. fontSize: '11px',
  10771. textAlign: 'center'
  10772. }, options.style || {}));
  10773. }
  10774. }
  10775. if (horiz) {
  10776. posx = snap ? (point.plotX || 0) + left : e.chartX;
  10777. posy = top + (opposite ? 0 : this.height);
  10778. }
  10779. else {
  10780. posx = opposite ? this.width + left : 0;
  10781. posy = snap ? (point.plotY || 0) + top : e.chartY;
  10782. }
  10783. if (!formatOption && !options.formatter) {
  10784. if (this.dateTime) {
  10785. formatFormat = '%b %d, %Y';
  10786. }
  10787. formatOption =
  10788. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  10789. }
  10790. // Show the label
  10791. var value = snap ?
  10792. (this.isXAxis ? point.x : point.y) :
  10793. this.toValue(horiz ? e.chartX : e.chartY);
  10794. // Crosshair should be rendered within Axis range (#7219). Also, the point
  10795. // of currentPriceIndicator should be inside the plot area, #14879.
  10796. var isInside = point ?
  10797. point.series.isPointInside(point) :
  10798. (isNumber(value) && value > min && value < max);
  10799. var text = '';
  10800. if (formatOption) {
  10801. text = format(formatOption, { value: value }, chart);
  10802. }
  10803. else if (options.formatter && isNumber(value)) {
  10804. text = options.formatter.call(this, value);
  10805. }
  10806. crossLabel.attr({
  10807. text: text,
  10808. x: posx,
  10809. y: posy,
  10810. visibility: isInside ? 'visible' : 'hidden'
  10811. });
  10812. crossBox = crossLabel.getBBox();
  10813. // now it is placed we can correct its position
  10814. if (isNumber(crossLabel.y)) {
  10815. if (horiz) {
  10816. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  10817. posy = crossLabel.y - crossBox.height;
  10818. }
  10819. }
  10820. else {
  10821. posy = crossLabel.y - (crossBox.height / 2);
  10822. }
  10823. }
  10824. // check the edges
  10825. if (horiz) {
  10826. limit = {
  10827. left: left - crossBox.x,
  10828. right: left + this.width - crossBox.x
  10829. };
  10830. }
  10831. else {
  10832. limit = {
  10833. left: this.labelAlign === 'left' ? left : 0,
  10834. right: this.labelAlign === 'right' ?
  10835. left + this.width :
  10836. chart.chartWidth
  10837. };
  10838. }
  10839. // left edge
  10840. if (crossLabel.translateX < limit.left) {
  10841. offset = limit.left - crossLabel.translateX;
  10842. }
  10843. // right edge
  10844. if (crossLabel.translateX + crossBox.width >= limit.right) {
  10845. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  10846. }
  10847. // show the crosslabel
  10848. crossLabel.attr({
  10849. x: posx + offset,
  10850. y: posy,
  10851. // First set x and y, then anchorX and anchorY, when box is actually
  10852. // calculated, #5702
  10853. anchorX: horiz ?
  10854. posx :
  10855. (this.opposite ? 0 : chart.chartWidth),
  10856. anchorY: horiz ?
  10857. (this.opposite ? chart.chartHeight : 0) :
  10858. posy + crossBox.height / 2
  10859. });
  10860. });
  10861. /* ************************************************************************** *
  10862. * Start value compare logic *
  10863. * ************************************************************************** */
  10864. /**
  10865. * Extend series.init by adding a method to modify the y value used for plotting
  10866. * on the y axis. This method is called both from the axis when finding dataMin
  10867. * and dataMax, and from the series.translate method.
  10868. *
  10869. * @ignore
  10870. * @function Highcharts.Series#init
  10871. */
  10872. Series.prototype.init = function () {
  10873. // Call base method
  10874. seriesInit.apply(this, arguments);
  10875. // Set comparison mode
  10876. this.initCompare(this.options.compare);
  10877. };
  10878. /**
  10879. * Highcharts Stock only. Set the
  10880. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  10881. * mode of the series after render time. In most cases it is more useful running
  10882. * {@link Axis#setCompare} on the X axis to update all its series.
  10883. *
  10884. * @function Highcharts.Series#setCompare
  10885. *
  10886. * @param {string} [compare]
  10887. * Can be one of `null` (default), `"percent"` or `"value"`.
  10888. */
  10889. Series.prototype.setCompare = function (compare) {
  10890. this.initCompare(compare);
  10891. // Survive to export, #5485
  10892. this.userOptions.compare = compare;
  10893. };
  10894. /**
  10895. * @ignore
  10896. * @function Highcharts.Series#initCompare
  10897. *
  10898. * @param {string} [compare]
  10899. * Can be one of `null` (default), `"percent"` or `"value"`.
  10900. */
  10901. Series.prototype.initCompare = function (compare) {
  10902. // Set or unset the modifyValue method
  10903. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  10904. function (value, point) {
  10905. var compareValue = this.compareValue;
  10906. if (typeof value !== 'undefined' &&
  10907. typeof compareValue !== 'undefined') { // #2601, #5814
  10908. // Get the modified value
  10909. if (compare === 'value') {
  10910. value -= compareValue;
  10911. // Compare percent
  10912. }
  10913. else {
  10914. value = 100 * (value / compareValue) -
  10915. (this.options.compareBase === 100 ? 0 : 100);
  10916. }
  10917. // record for tooltip etc.
  10918. if (point) {
  10919. point.change = value;
  10920. }
  10921. return value;
  10922. }
  10923. return 0;
  10924. } :
  10925. null;
  10926. // Mark dirty
  10927. if (this.chart.hasRendered) {
  10928. this.isDirty = true;
  10929. }
  10930. };
  10931. /**
  10932. * Extend series.processData by finding the first y value in the plot area,
  10933. * used for comparing the following values
  10934. *
  10935. * @ignore
  10936. * @function Highcharts.Series#processData
  10937. */
  10938. Series.prototype.processData = function (force) {
  10939. var series = this,
  10940. i,
  10941. keyIndex = -1,
  10942. processedXData,
  10943. processedYData,
  10944. compareStart = series.options.compareStart === true ? 0 : 1,
  10945. length,
  10946. compareValue;
  10947. // call base method
  10948. seriesProcessData.apply(this, arguments);
  10949. if (series.xAxis && series.processedYData) { // not pies
  10950. // local variables
  10951. processedXData = series.processedXData;
  10952. processedYData = series.processedYData;
  10953. length = processedYData.length;
  10954. // For series with more than one value (range, OHLC etc), compare
  10955. // against close or the pointValKey (#4922, #3112, #9854)
  10956. if (series.pointArrayMap) {
  10957. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  10958. }
  10959. // find the first value for comparison
  10960. for (i = 0; i < length - compareStart; i++) {
  10961. compareValue = processedYData[i] && keyIndex > -1 ?
  10962. processedYData[i][keyIndex] :
  10963. processedYData[i];
  10964. if (isNumber(compareValue) &&
  10965. processedXData[i + compareStart] >=
  10966. series.xAxis.min &&
  10967. compareValue !== 0) {
  10968. series.compareValue = compareValue;
  10969. break;
  10970. }
  10971. }
  10972. }
  10973. return;
  10974. };
  10975. // Modify series extremes
  10976. addEvent(Series, 'afterGetExtremes', function (e) {
  10977. var dataExtremes = e.dataExtremes;
  10978. if (this.modifyValue && dataExtremes) {
  10979. var extremes = [
  10980. this.modifyValue(dataExtremes.dataMin),
  10981. this.modifyValue(dataExtremes.dataMax)
  10982. ];
  10983. dataExtremes.dataMin = arrayMin(extremes);
  10984. dataExtremes.dataMax = arrayMax(extremes);
  10985. }
  10986. });
  10987. /**
  10988. * Highcharts Stock only. Set the compare mode on all series
  10989. * belonging to an Y axis after render time.
  10990. *
  10991. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  10992. *
  10993. * @sample stock/members/axis-setcompare/
  10994. * Set compoare
  10995. *
  10996. * @function Highcharts.Axis#setCompare
  10997. *
  10998. * @param {string} [compare]
  10999. * The compare mode. Can be one of `null` (default), `"value"` or
  11000. * `"percent"`.
  11001. *
  11002. * @param {boolean} [redraw=true]
  11003. * Whether to redraw the chart or to wait for a later call to
  11004. * {@link Chart#redraw}.
  11005. */
  11006. Axis.prototype.setCompare = function (compare, redraw) {
  11007. if (!this.isXAxis) {
  11008. this.series.forEach(function (series) {
  11009. series.setCompare(compare);
  11010. });
  11011. if (pick(redraw, true)) {
  11012. this.chart.redraw();
  11013. }
  11014. }
  11015. };
  11016. /**
  11017. * Extend the tooltip formatter by adding support for the point.change variable
  11018. * as well as the changeDecimals option.
  11019. *
  11020. * @ignore
  11021. * @function Highcharts.Point#tooltipFormatter
  11022. *
  11023. * @param {string} pointFormat
  11024. */
  11025. Point.prototype.tooltipFormatter = function (pointFormat) {
  11026. var point = this;
  11027. var numberFormatter = point.series.chart.numberFormatter;
  11028. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  11029. return pointTooltipFormatter.apply(this, [pointFormat]);
  11030. };
  11031. /* ************************************************************************** *
  11032. * End value compare logic *
  11033. * ************************************************************************** */
  11034. // Extend the Series prototype to create a separate series clip box. This is
  11035. // related to using multiple panes, and a future pane logic should incorporate
  11036. // this feature (#2754).
  11037. addEvent(Series, 'render', function () {
  11038. var chart = this.chart,
  11039. clipHeight;
  11040. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  11041. // if the series type handles clipping in the animate method (#2975).
  11042. if (!(chart.is3d && chart.is3d()) &&
  11043. !chart.polar &&
  11044. this.xAxis &&
  11045. !this.xAxis.isRadial && // Gauge, #6192
  11046. this.options.clip !== false // #15128
  11047. ) {
  11048. clipHeight = this.yAxis.len;
  11049. // Include xAxis line width (#8031) but only if the Y axis ends on the
  11050. // edge of the X axis (#11005).
  11051. if (this.xAxis.axisLine) {
  11052. var dist = chart.plotTop + chart.plotHeight -
  11053. this.yAxis.pos - this.yAxis.len,
  11054. lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  11055. if (dist >= 0) {
  11056. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  11057. }
  11058. }
  11059. // First render, initial clip box. clipBox also needs to be updated if
  11060. // the series is rendered again before starting animating, in
  11061. // compliance with a responsive rule (#13858).
  11062. if (!chart.hasRendered || (!this.clipBox && this.isDirty && !this.isDirtyData)) {
  11063. this.clipBox = this.clipBox || merge(chart.clipBox);
  11064. this.clipBox.width = this.xAxis.len;
  11065. this.clipBox.height = clipHeight;
  11066. }
  11067. if (chart.hasRendered) {
  11068. var animation = animObject(this.options.animation);
  11069. // #15435: this.sharedClipKey might not have been set yet, for
  11070. // example when updating the series, so we need to use this
  11071. // function instead
  11072. var sharedClipKey = this.getSharedClipKey(animation);
  11073. var clipRect = chart.sharedClips[sharedClipKey];
  11074. // On redrawing, resizing etc, update the clip rectangle.
  11075. //
  11076. // #15435: Update it even when we are creating/updating clipBox,
  11077. // since there could be series updating and pane size changes
  11078. // happening at the same time and we dont destroy shared clips in
  11079. // stock.
  11080. if (clipRect) {
  11081. // animate in case resize is done during initial animation
  11082. clipRect.animate({
  11083. width: this.xAxis.len,
  11084. height: clipHeight
  11085. });
  11086. var markerClipRect = chart.sharedClips[sharedClipKey + 'm'];
  11087. // also change markers clip animation for consistency
  11088. // (marker clip rects should exist only on chart init)
  11089. if (markerClipRect) {
  11090. markerClipRect.animate({
  11091. width: this.xAxis.len
  11092. });
  11093. }
  11094. }
  11095. }
  11096. }
  11097. });
  11098. addEvent(Chart, 'update', function (e) {
  11099. var options = e.options;
  11100. // Use case: enabling scrollbar from a disabled state.
  11101. // Scrollbar needs to be initialized from a controller, Navigator in this
  11102. // case (#6615)
  11103. if ('scrollbar' in options && this.navigator) {
  11104. merge(true, this.options.scrollbar, options.scrollbar);
  11105. this.navigator.update({}, false);
  11106. delete options.scrollbar;
  11107. }
  11108. });
  11109. /* *
  11110. *
  11111. * Default Export
  11112. *
  11113. * */
  11114. /* *
  11115. *
  11116. * API Options
  11117. *
  11118. * */
  11119. /**
  11120. * Compare the values of the series against the first non-null, non-
  11121. * zero value in the visible range. The y axis will show percentage
  11122. * or absolute change depending on whether `compare` is set to `"percent"`
  11123. * or `"value"`. When this is applied to multiple series, it allows
  11124. * comparing the development of the series against each other. Adds
  11125. * a `change` field to every point object.
  11126. *
  11127. * @see [compareBase](#plotOptions.series.compareBase)
  11128. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  11129. *
  11130. * @sample {highstock} stock/plotoptions/series-compare-percent/
  11131. * Percent
  11132. * @sample {highstock} stock/plotoptions/series-compare-value/
  11133. * Value
  11134. *
  11135. * @type {string}
  11136. * @since 1.0.1
  11137. * @product highstock
  11138. * @apioption plotOptions.series.compare
  11139. */
  11140. /**
  11141. * Defines if comparison should start from the first point within the visible
  11142. * range or should start from the first point **before** the range.
  11143. *
  11144. * In other words, this flag determines if first point within the visible range
  11145. * will have 0% (`compareStart=true`) or should have been already calculated
  11146. * according to the previous point (`compareStart=false`).
  11147. *
  11148. * @sample {highstock} stock/plotoptions/series-comparestart/
  11149. * Calculate compare within visible range
  11150. *
  11151. * @type {boolean}
  11152. * @default false
  11153. * @since 6.0.0
  11154. * @product highstock
  11155. * @apioption plotOptions.series.compareStart
  11156. */
  11157. /**
  11158. * When [compare](#plotOptions.series.compare) is `percent`, this option
  11159. * dictates whether to use 0 or 100 as the base of comparison.
  11160. *
  11161. * @sample {highstock} stock/plotoptions/series-comparebase/
  11162. * Compare base is 100
  11163. *
  11164. * @type {number}
  11165. * @default 0
  11166. * @since 5.0.6
  11167. * @product highstock
  11168. * @validvalue [0, 100]
  11169. * @apioption plotOptions.series.compareBase
  11170. */
  11171. ''; // keeps doclets above in transpiled file
  11172. return StockChart;
  11173. });
  11174. _registerModule(_modules, 'masters/modules/stock.src.js', [_modules['Core/Globals.js'], _modules['Core/Chart/StockChart.js']], function (Highcharts, StockChart) {
  11175. Highcharts.StockChart = Highcharts.stockChart = StockChart.stockChart;
  11176. });
  11177. }));