highcharts.src.js 2.0 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741
  1. /**
  2. * @license Highcharts JS v11.1.0 (2023-06-05)
  3. *
  4. * (c) 2009-2021 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. (function (root, factory) {
  9. if (typeof module === 'object' && module.exports) {
  10. factory['default'] = factory;
  11. module.exports = root.document ?
  12. factory(root) :
  13. factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define('highcharts/highcharts', function () {
  16. return factory(root);
  17. });
  18. } else {
  19. if (root.Highcharts) {
  20. root.Highcharts.error(16, true);
  21. }
  22. root.Highcharts = factory(root);
  23. }
  24. }(typeof window !== 'undefined' ? window : this, function (window) {
  25. 'use strict';
  26. var _modules = {};
  27. function _registerModule(obj, path, args, fn) {
  28. if (!obj.hasOwnProperty(path)) {
  29. obj[path] = fn.apply(null, args);
  30. if (typeof CustomEvent === 'function') {
  31. window.dispatchEvent(
  32. new CustomEvent(
  33. 'HighchartsModuleLoaded',
  34. { detail: { path: path, module: obj[path] }
  35. })
  36. );
  37. }
  38. }
  39. }
  40. _registerModule(_modules, 'Core/Globals.js', [], function () {
  41. /* *
  42. *
  43. * (c) 2010-2021 Torstein Honsi
  44. *
  45. * License: www.highcharts.com/license
  46. *
  47. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48. *
  49. * */
  50. /* *
  51. *
  52. * Namespace
  53. *
  54. * */
  55. /**
  56. * Shared Highcharts properties.
  57. * @private
  58. */
  59. var Globals;
  60. (function (Globals) {
  61. /* *
  62. *
  63. * Constants
  64. *
  65. * */
  66. Globals.SVG_NS = 'http://www.w3.org/2000/svg', Globals.product = 'Highcharts', Globals.version = '11.1.0', Globals.win = (typeof window !== 'undefined' ?
  67. window :
  68. {}), // eslint-disable-line node/no-unsupported-features/es-builtins
  69. Globals.doc = Globals.win.document, Globals.svg = (Globals.doc &&
  70. Globals.doc.createElementNS &&
  71. !!Globals.doc.createElementNS(Globals.SVG_NS, 'svg').createSVGRect), Globals.userAgent = (Globals.win.navigator && Globals.win.navigator.userAgent) || '', Globals.isChrome = Globals.userAgent.indexOf('Chrome') !== -1, Globals.isFirefox = Globals.userAgent.indexOf('Firefox') !== -1, Globals.isMS = /(edge|msie|trident)/i.test(Globals.userAgent) && !Globals.win.opera, Globals.isSafari = !Globals.isChrome && Globals.userAgent.indexOf('Safari') !== -1, Globals.isTouchDevice = /(Mobile|Android|Windows Phone)/.test(Globals.userAgent), Globals.isWebKit = Globals.userAgent.indexOf('AppleWebKit') !== -1, Globals.deg2rad = Math.PI * 2 / 360, Globals.hasBidiBug = (Globals.isFirefox &&
  72. parseInt(Globals.userAgent.split('Firefox/')[1], 10) < 4 // issue #38
  73. ), Globals.hasTouch = !!Globals.win.TouchEvent, Globals.marginNames = [
  74. 'plotTop',
  75. 'marginRight',
  76. 'marginBottom',
  77. 'plotLeft'
  78. ], Globals.noop = function () { }, Globals.supportsPassiveEvents = (function () {
  79. // Checks whether the browser supports passive events, (#11353).
  80. let supportsPassive = false;
  81. // Object.defineProperty doesn't work on IE as well as passive
  82. // events - instead of using polyfill, we can exclude IE totally.
  83. if (!Globals.isMS) {
  84. const opts = Object.defineProperty({}, 'passive', {
  85. get: function () {
  86. supportsPassive = true;
  87. }
  88. });
  89. if (Globals.win.addEventListener && Globals.win.removeEventListener) {
  90. Globals.win.addEventListener('testPassive', Globals.noop, opts);
  91. Globals.win.removeEventListener('testPassive', Globals.noop, opts);
  92. }
  93. }
  94. return supportsPassive;
  95. }());
  96. /**
  97. * An array containing the current chart objects in the page. A chart's
  98. * position in the array is preserved throughout the page's lifetime. When
  99. * a chart is destroyed, the array item becomes `undefined`.
  100. *
  101. * @name Highcharts.charts
  102. * @type {Array<Highcharts.Chart|undefined>}
  103. */
  104. Globals.charts = [];
  105. /**
  106. * A hook for defining additional date format specifiers. New
  107. * specifiers are defined as key-value pairs by using the
  108. * specifier as key, and a function which takes the timestamp as
  109. * value. This function returns the formatted portion of the
  110. * date.
  111. *
  112. * @sample highcharts/global/dateformats/
  113. * Adding support for week number
  114. *
  115. * @name Highcharts.dateFormats
  116. * @type {Record<string, Highcharts.TimeFormatCallbackFunction>}
  117. */
  118. Globals.dateFormats = {};
  119. /**
  120. * @private
  121. * @deprecated
  122. * @todo Use only `Core/Series/SeriesRegistry.seriesTypes`
  123. */
  124. Globals.seriesTypes = {};
  125. /**
  126. * @private
  127. */
  128. Globals.symbolSizes = {};
  129. /* *
  130. *
  131. * Properties
  132. *
  133. * */
  134. // eslint-disable-next-line prefer-const
  135. Globals.chartCount = 0;
  136. })(Globals || (Globals = {}));
  137. /* *
  138. *
  139. * Default Export
  140. *
  141. * */
  142. /* *
  143. *
  144. * API Declarations
  145. *
  146. * */
  147. /**
  148. * Theme options that should get applied to the chart. In module mode it
  149. * might not be possible to change this property because of read-only
  150. * restrictions, instead use {@link Highcharts.setOptions}.
  151. *
  152. * @deprecated
  153. * @name Highcharts.theme
  154. * @type {Highcharts.Options}
  155. */
  156. (''); // keeps doclets above in JS file
  157. return Globals;
  158. });
  159. _registerModule(_modules, 'Core/Utilities.js', [_modules['Core/Globals.js']], function (H) {
  160. /* *
  161. *
  162. * (c) 2010-2021 Torstein Honsi
  163. *
  164. * License: www.highcharts.com/license
  165. *
  166. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  167. *
  168. * */
  169. const { charts, doc, win } = H;
  170. /* *
  171. *
  172. * Functions
  173. *
  174. * */
  175. /**
  176. * Provide error messages for debugging, with links to online explanation. This
  177. * function can be overridden to provide custom error handling.
  178. *
  179. * @sample highcharts/chart/highcharts-error/
  180. * Custom error handler
  181. *
  182. * @function Highcharts.error
  183. *
  184. * @param {number|string} code
  185. * The error code. See
  186. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  187. * for available codes. If it is a string, the error message is printed
  188. * directly in the console.
  189. *
  190. * @param {boolean} [stop=false]
  191. * Whether to throw an error or just log a warning in the console.
  192. *
  193. * @param {Highcharts.Chart} [chart]
  194. * Reference to the chart that causes the error. Used in 'debugger'
  195. * module to display errors directly on the chart.
  196. * Important note: This argument is undefined for errors that lack
  197. * access to the Chart instance. In such case, the error will be
  198. * displayed on the last created chart.
  199. *
  200. * @param {Highcharts.Dictionary<string>} [params]
  201. * Additional parameters for the generated message.
  202. *
  203. * @return {void}
  204. */
  205. function error(code, stop, chart, params) {
  206. const severity = stop ? 'Highcharts error' : 'Highcharts warning';
  207. if (code === 32) {
  208. code = `${severity}: Deprecated member`;
  209. }
  210. const isCode = isNumber(code);
  211. let message = isCode ?
  212. `${severity} #${code}: www.highcharts.com/errors/${code}/` :
  213. code.toString();
  214. const defaultHandler = function () {
  215. if (stop) {
  216. throw new Error(message);
  217. }
  218. // else ...
  219. if (win.console &&
  220. error.messages.indexOf(message) === -1 // prevent console flooting
  221. ) {
  222. console.warn(message); // eslint-disable-line no-console
  223. }
  224. };
  225. if (typeof params !== 'undefined') {
  226. let additionalMessages = '';
  227. if (isCode) {
  228. message += '?';
  229. }
  230. objectEach(params, function (value, key) {
  231. additionalMessages += `\n - ${key}: ${value}`;
  232. if (isCode) {
  233. message += encodeURI(key) + '=' + encodeURI(value);
  234. }
  235. });
  236. message += additionalMessages;
  237. }
  238. fireEvent(H, 'displayError', { chart, code, message, params }, defaultHandler);
  239. error.messages.push(message);
  240. }
  241. (function (error) {
  242. error.messages = [];
  243. })(error || (error = {}));
  244. /* eslint-disable valid-jsdoc */
  245. /**
  246. * Utility function to deep merge two or more objects and return a third object.
  247. * If the first argument is true, the contents of the second object is copied
  248. * into the first object. The merge function can also be used with a single
  249. * object argument to create a deep copy of an object.
  250. *
  251. * @function Highcharts.merge<T>
  252. *
  253. * @param {boolean} extend
  254. * Whether to extend the left-side object (a) or return a whole new
  255. * object.
  256. *
  257. * @param {T|undefined} a
  258. * The first object to extend. When only this is given, the function
  259. * returns a deep copy.
  260. *
  261. * @param {...Array<object|undefined>} [n]
  262. * An object to merge into the previous one.
  263. *
  264. * @return {T}
  265. * The merged object. If the first argument is true, the return is the
  266. * same as the second argument.
  267. */ /**
  268. * Utility function to deep merge two or more objects and return a third object.
  269. * The merge function can also be used with a single object argument to create a
  270. * deep copy of an object.
  271. *
  272. * @function Highcharts.merge<T>
  273. *
  274. * @param {T|undefined} a
  275. * The first object to extend. When only this is given, the function
  276. * returns a deep copy.
  277. *
  278. * @param {...Array<object|undefined>} [n]
  279. * An object to merge into the previous one.
  280. *
  281. * @return {T}
  282. * The merged object. If the first argument is true, the return is the
  283. * same as the second argument.
  284. */
  285. function merge() {
  286. /* eslint-enable valid-jsdoc */
  287. let i, args = arguments, ret = {};
  288. const doCopy = function (copy, original) {
  289. // An object is replacing a primitive
  290. if (typeof copy !== 'object') {
  291. copy = {};
  292. }
  293. objectEach(original, function (value, key) {
  294. // Prototype pollution (#14883)
  295. if (key === '__proto__' || key === 'constructor') {
  296. return;
  297. }
  298. // Copy the contents of objects, but not arrays or DOM nodes
  299. if (isObject(value, true) &&
  300. !isClass(value) &&
  301. !isDOMElement(value)) {
  302. copy[key] = doCopy(copy[key] || {}, value);
  303. // Primitives and arrays are copied over directly
  304. }
  305. else {
  306. copy[key] = original[key];
  307. }
  308. });
  309. return copy;
  310. };
  311. // If first argument is true, copy into the existing object. Used in
  312. // setOptions.
  313. if (args[0] === true) {
  314. ret = args[1];
  315. args = Array.prototype.slice.call(args, 2);
  316. }
  317. // For each argument, extend the return
  318. const len = args.length;
  319. for (i = 0; i < len; i++) {
  320. ret = doCopy(ret, args[i]);
  321. }
  322. return ret;
  323. }
  324. /**
  325. * Constrain a value to within a lower and upper threshold.
  326. *
  327. * @private
  328. * @param {number} value The initial value
  329. * @param {number} min The lower threshold
  330. * @param {number} max The upper threshold
  331. * @return {number} Returns a number value within min and max.
  332. */
  333. function clamp(value, min, max) {
  334. return value > min ? value < max ? value : max : min;
  335. }
  336. // eslint-disable-next-line valid-jsdoc
  337. /**
  338. * Return the deep difference between two objects. It can either return the new
  339. * properties, or optionally return the old values of new properties.
  340. * @private
  341. */
  342. function diffObjects(newer, older, keepOlder, collectionsWithUpdate) {
  343. const ret = {};
  344. /**
  345. * Recurse over a set of options and its current values, and store the
  346. * current values in the ret object.
  347. */
  348. function diff(newer, older, ret, depth) {
  349. const keeper = keepOlder ? older : newer;
  350. objectEach(newer, function (newerVal, key) {
  351. if (!depth &&
  352. collectionsWithUpdate &&
  353. collectionsWithUpdate.indexOf(key) > -1 &&
  354. older[key]) {
  355. newerVal = splat(newerVal);
  356. ret[key] = [];
  357. // Iterate over collections like series, xAxis or yAxis and map
  358. // the items by index.
  359. for (let i = 0; i < Math.max(newerVal.length, older[key].length); i++) {
  360. // Item exists in current data (#6347)
  361. if (older[key][i]) {
  362. // If the item is missing from the new data, we need to
  363. // save the whole config structure. Like when
  364. // responsively updating from a dual axis layout to a
  365. // single axis and back (#13544).
  366. if (newerVal[i] === void 0) {
  367. ret[key][i] = older[key][i];
  368. // Otherwise, proceed
  369. }
  370. else {
  371. ret[key][i] = {};
  372. diff(newerVal[i], older[key][i], ret[key][i], depth + 1);
  373. }
  374. }
  375. }
  376. }
  377. else if (isObject(newerVal, true) &&
  378. !newerVal.nodeType // #10044
  379. ) {
  380. ret[key] = isArray(newerVal) ? [] : {};
  381. diff(newerVal, older[key] || {}, ret[key], depth + 1);
  382. // Delete empty nested objects
  383. if (Object.keys(ret[key]).length === 0 &&
  384. // Except colorAxis which is a special case where the empty
  385. // object means it is enabled. Which is unfortunate and we
  386. // should try to find a better way.
  387. !(key === 'colorAxis' && depth === 0)) {
  388. delete ret[key];
  389. }
  390. }
  391. else if (newer[key] !== older[key] ||
  392. // If the newer key is explicitly undefined, keep it (#10525)
  393. (key in newer && !(key in older))) {
  394. ret[key] = keeper[key];
  395. }
  396. });
  397. }
  398. diff(newer, older, ret, 0);
  399. return ret;
  400. }
  401. /**
  402. * Shortcut for parseInt
  403. *
  404. * @private
  405. * @function Highcharts.pInt
  406. *
  407. * @param {*} s
  408. * any
  409. *
  410. * @param {number} [mag]
  411. * Magnitude
  412. *
  413. * @return {number}
  414. * number
  415. */
  416. function pInt(s, mag) {
  417. return parseInt(s, mag || 10);
  418. }
  419. /**
  420. * Utility function to check for string type.
  421. *
  422. * @function Highcharts.isString
  423. *
  424. * @param {*} s
  425. * The item to check.
  426. *
  427. * @return {boolean}
  428. * True if the argument is a string.
  429. */
  430. function isString(s) {
  431. return typeof s === 'string';
  432. }
  433. /**
  434. * Utility function to check if an item is an array.
  435. *
  436. * @function Highcharts.isArray
  437. *
  438. * @param {*} obj
  439. * The item to check.
  440. *
  441. * @return {boolean}
  442. * True if the argument is an array.
  443. */
  444. function isArray(obj) {
  445. const str = Object.prototype.toString.call(obj);
  446. return str === '[object Array]' || str === '[object Array Iterator]';
  447. }
  448. /**
  449. * Utility function to check if an item is of type object.
  450. *
  451. * @function Highcharts.isObject
  452. *
  453. * @param {*} obj
  454. * The item to check.
  455. *
  456. * @param {boolean} [strict=false]
  457. * Also checks that the object is not an array.
  458. *
  459. * @return {boolean}
  460. * True if the argument is an object.
  461. */
  462. function isObject(obj, strict) {
  463. return (!!obj &&
  464. typeof obj === 'object' &&
  465. (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
  466. }
  467. /**
  468. * Utility function to check if an Object is a HTML Element.
  469. *
  470. * @function Highcharts.isDOMElement
  471. *
  472. * @param {*} obj
  473. * The item to check.
  474. *
  475. * @return {boolean}
  476. * True if the argument is a HTML Element.
  477. */
  478. function isDOMElement(obj) {
  479. return isObject(obj) && typeof obj.nodeType === 'number';
  480. }
  481. /**
  482. * Utility function to check if an Object is a class.
  483. *
  484. * @function Highcharts.isClass
  485. *
  486. * @param {object|undefined} obj
  487. * The item to check.
  488. *
  489. * @return {boolean}
  490. * True if the argument is a class.
  491. */
  492. function isClass(obj) {
  493. const c = obj && obj.constructor;
  494. return !!(isObject(obj, true) &&
  495. !isDOMElement(obj) &&
  496. (c && c.name && c.name !== 'Object'));
  497. }
  498. /**
  499. * Utility function to check if an item is a number and it is finite (not NaN,
  500. * Infinity or -Infinity).
  501. *
  502. * @function Highcharts.isNumber
  503. *
  504. * @param {*} n
  505. * The item to check.
  506. *
  507. * @return {boolean}
  508. * True if the item is a finite number
  509. */
  510. function isNumber(n) {
  511. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  512. }
  513. /**
  514. * Remove the last occurence of an item from an array.
  515. *
  516. * @function Highcharts.erase
  517. *
  518. * @param {Array<*>} arr
  519. * The array.
  520. *
  521. * @param {*} item
  522. * The item to remove.
  523. *
  524. * @return {void}
  525. */
  526. function erase(arr, item) {
  527. let i = arr.length;
  528. while (i--) {
  529. if (arr[i] === item) {
  530. arr.splice(i, 1);
  531. break;
  532. }
  533. }
  534. }
  535. /**
  536. * Insert a series or an axis in a collection with other items, either the
  537. * chart series or yAxis series or axis collections, in the correct order
  538. * according to the index option and whether it is internal. Used internally
  539. * when adding series and axes.
  540. *
  541. * @private
  542. * @function Highcharts.Chart#insertItem
  543. * @param {Highcharts.Series|Highcharts.Axis} item
  544. * The item to insert
  545. * @param {Array<Highcharts.Series>|Array<Highcharts.Axis>} collection
  546. * A collection of items, like `chart.series` or `xAxis.series`.
  547. * @return {number} The index of the series in the collection.
  548. */
  549. function insertItem(item, collection) {
  550. const indexOption = item.options.index, length = collection.length;
  551. let i;
  552. for (
  553. // Internal item (navigator) should always be pushed to the end
  554. i = item.options.isInternal ? length : 0; i < length + 1; i++) {
  555. if (
  556. // No index option, reached the end of the collection,
  557. // equivalent to pushing
  558. !collection[i] ||
  559. // Handle index option, the element to insert has lower index
  560. (isNumber(indexOption) &&
  561. indexOption < pick(collection[i].options.index, collection[i]._i)) ||
  562. // Insert the new item before other internal items
  563. // (navigator)
  564. collection[i].options.isInternal) {
  565. collection.splice(i, 0, item);
  566. break;
  567. }
  568. }
  569. return i;
  570. }
  571. /**
  572. * Adds an item to an array, if it is not present in the array.
  573. *
  574. * @function Highcharts.pushUnique
  575. *
  576. * @param {Array<unknown>} array
  577. * The array to add the item to.
  578. *
  579. * @param {unknown} item
  580. * The item to add.
  581. *
  582. * @return {boolean}
  583. * Returns true, if the item was not present and has been added.
  584. */
  585. function pushUnique(array, item) {
  586. return array.indexOf(item) < 0 && !!array.push(item);
  587. }
  588. /**
  589. * Check if an object is null or undefined.
  590. *
  591. * @function Highcharts.defined
  592. *
  593. * @param {*} obj
  594. * The object to check.
  595. *
  596. * @return {boolean}
  597. * False if the object is null or undefined, otherwise true.
  598. */
  599. function defined(obj) {
  600. return typeof obj !== 'undefined' && obj !== null;
  601. }
  602. /**
  603. * Set or get an attribute or an object of attributes.
  604. *
  605. * To use as a setter, pass a key and a value, or let the second argument be a
  606. * collection of keys and values. When using a collection, passing a value of
  607. * `null` or `undefined` will remove the attribute.
  608. *
  609. * To use as a getter, pass only a string as the second argument.
  610. *
  611. * @function Highcharts.attr
  612. *
  613. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  614. * The DOM element to receive the attribute(s).
  615. *
  616. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [keyOrAttribs]
  617. * The property or an object of key-value pairs.
  618. *
  619. * @param {number|string} [value]
  620. * The value if a single property is set.
  621. *
  622. * @return {string|null|undefined}
  623. * When used as a getter, return the value.
  624. */
  625. function attr(elem, keyOrAttribs, value) {
  626. const isGetter = isString(keyOrAttribs) && !defined(value);
  627. let ret;
  628. const attrSingle = (value, key) => {
  629. // Set the value
  630. if (defined(value)) {
  631. elem.setAttribute(key, value);
  632. // Get the value
  633. }
  634. else if (isGetter) {
  635. ret = elem.getAttribute(key);
  636. // IE7 and below cannot get class through getAttribute (#7850)
  637. if (!ret && key === 'class') {
  638. ret = elem.getAttribute(key + 'Name');
  639. }
  640. // Remove the value
  641. }
  642. else {
  643. elem.removeAttribute(key);
  644. }
  645. };
  646. // If keyOrAttribs is a string
  647. if (isString(keyOrAttribs)) {
  648. attrSingle(value, keyOrAttribs);
  649. // Else if keyOrAttribs is defined, it is a hash of key/value pairs
  650. }
  651. else {
  652. objectEach(keyOrAttribs, attrSingle);
  653. }
  654. return ret;
  655. }
  656. /**
  657. * Check if an element is an array, and if not, make it into an array.
  658. *
  659. * @function Highcharts.splat
  660. *
  661. * @param {*} obj
  662. * The object to splat.
  663. *
  664. * @return {Array}
  665. * The produced or original array.
  666. */
  667. function splat(obj) {
  668. return isArray(obj) ? obj : [obj];
  669. }
  670. /**
  671. * Set a timeout if the delay is given, otherwise perform the function
  672. * synchronously.
  673. *
  674. * @function Highcharts.syncTimeout
  675. *
  676. * @param {Function} fn
  677. * The function callback.
  678. *
  679. * @param {number} delay
  680. * Delay in milliseconds.
  681. *
  682. * @param {*} [context]
  683. * An optional context to send to the function callback.
  684. *
  685. * @return {number}
  686. * An identifier for the timeout that can later be cleared with
  687. * Highcharts.clearTimeout. Returns -1 if there is no timeout.
  688. */
  689. function syncTimeout(fn, delay, context) {
  690. if (delay > 0) {
  691. return setTimeout(fn, delay, context);
  692. }
  693. fn.call(0, context);
  694. return -1;
  695. }
  696. /**
  697. * Internal clear timeout. The function checks that the `id` was not removed
  698. * (e.g. by `chart.destroy()`). For the details see
  699. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  700. *
  701. * @function Highcharts.clearTimeout
  702. *
  703. * @param {number|undefined} id
  704. * Id of a timeout.
  705. */
  706. function internalClearTimeout(id) {
  707. if (defined(id)) {
  708. clearTimeout(id);
  709. }
  710. }
  711. /* eslint-disable valid-jsdoc */
  712. /**
  713. * Utility function to extend an object with the members of another.
  714. *
  715. * @function Highcharts.extend<T>
  716. *
  717. * @param {T|undefined} a
  718. * The object to be extended.
  719. *
  720. * @param {Partial<T>} b
  721. * The object to add to the first one.
  722. *
  723. * @return {T}
  724. * Object a, the original object.
  725. */
  726. function extend(a, b) {
  727. /* eslint-enable valid-jsdoc */
  728. let n;
  729. if (!a) {
  730. a = {};
  731. }
  732. for (n in b) { // eslint-disable-line guard-for-in
  733. a[n] = b[n];
  734. }
  735. return a;
  736. }
  737. /* eslint-disable valid-jsdoc */
  738. /**
  739. * Return the first value that is not null or undefined.
  740. *
  741. * @function Highcharts.pick<T>
  742. *
  743. * @param {...Array<T|null|undefined>} items
  744. * Variable number of arguments to inspect.
  745. *
  746. * @return {T}
  747. * The value of the first argument that is not null or undefined.
  748. */
  749. function pick() {
  750. const args = arguments;
  751. const length = args.length;
  752. for (let i = 0; i < length; i++) {
  753. const arg = args[i];
  754. if (typeof arg !== 'undefined' && arg !== null) {
  755. return arg;
  756. }
  757. }
  758. }
  759. /**
  760. * Set CSS on a given element.
  761. *
  762. * @function Highcharts.css
  763. *
  764. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
  765. * An HTML DOM element.
  766. *
  767. * @param {Highcharts.CSSObject} styles
  768. * Style object with camel case property names.
  769. *
  770. * @return {void}
  771. */
  772. function css(el, styles) {
  773. if (H.isMS && !H.svg) { // #2686
  774. if (styles && defined(styles.opacity)) {
  775. styles.filter = `alpha(opacity=${styles.opacity * 100})`;
  776. }
  777. }
  778. extend(el.style, styles);
  779. }
  780. /**
  781. * Utility function to create an HTML element with attributes and styles.
  782. *
  783. * @function Highcharts.createElement
  784. *
  785. * @param {string} tag
  786. * The HTML tag.
  787. *
  788. * @param {Highcharts.HTMLAttributes} [attribs]
  789. * Attributes as an object of key-value pairs.
  790. *
  791. * @param {Highcharts.CSSObject} [styles]
  792. * Styles as an object of key-value pairs.
  793. *
  794. * @param {Highcharts.HTMLDOMElement} [parent]
  795. * The parent HTML object.
  796. *
  797. * @param {boolean} [nopad=false]
  798. * If true, remove all padding, border and margin.
  799. *
  800. * @return {Highcharts.HTMLDOMElement}
  801. * The created DOM element.
  802. */
  803. function createElement(tag, attribs, styles, parent, nopad) {
  804. const el = doc.createElement(tag);
  805. if (attribs) {
  806. extend(el, attribs);
  807. }
  808. if (nopad) {
  809. css(el, { padding: '0', border: 'none', margin: '0' });
  810. }
  811. if (styles) {
  812. css(el, styles);
  813. }
  814. if (parent) {
  815. parent.appendChild(el);
  816. }
  817. return el;
  818. }
  819. // eslint-disable-next-line valid-jsdoc
  820. /**
  821. * Extend a prototyped class by new members.
  822. *
  823. * @deprecated
  824. * @function Highcharts.extendClass<T>
  825. *
  826. * @param {Highcharts.Class<T>} parent
  827. * The parent prototype to inherit.
  828. *
  829. * @param {Highcharts.Dictionary<*>} members
  830. * A collection of prototype members to add or override compared to the
  831. * parent prototype.
  832. *
  833. * @return {Highcharts.Class<T>}
  834. * A new prototype.
  835. */
  836. function extendClass(parent, members) {
  837. const obj = (function () { });
  838. obj.prototype = new parent(); // eslint-disable-line new-cap
  839. extend(obj.prototype, members);
  840. return obj;
  841. }
  842. /**
  843. * Left-pad a string to a given length by adding a character repetitively.
  844. *
  845. * @function Highcharts.pad
  846. *
  847. * @param {number} number
  848. * The input string or number.
  849. *
  850. * @param {number} [length]
  851. * The desired string length.
  852. *
  853. * @param {string} [padder=0]
  854. * The character to pad with.
  855. *
  856. * @return {string}
  857. * The padded string.
  858. */
  859. function pad(number, length, padder) {
  860. return new Array((length || 2) +
  861. 1 -
  862. String(number)
  863. .replace('-', '')
  864. .length).join(padder || '0') + number;
  865. }
  866. /**
  867. * Return a length based on either the integer value, or a percentage of a base.
  868. *
  869. * @function Highcharts.relativeLength
  870. *
  871. * @param {Highcharts.RelativeSize} value
  872. * A percentage string or a number.
  873. *
  874. * @param {number} base
  875. * The full length that represents 100%.
  876. *
  877. * @param {number} [offset=0]
  878. * A pixel offset to apply for percentage values. Used internally in
  879. * axis positioning.
  880. *
  881. * @return {number}
  882. * The computed length.
  883. */
  884. function relativeLength(value, base, offset) {
  885. return (/%$/).test(value) ?
  886. (base * parseFloat(value) / 100) + (offset || 0) :
  887. parseFloat(value);
  888. }
  889. /**
  890. * Wrap a method with extended functionality, preserving the original function.
  891. *
  892. * @function Highcharts.wrap
  893. *
  894. * @param {*} obj
  895. * The context object that the method belongs to. In real cases, this is
  896. * often a prototype.
  897. *
  898. * @param {string} method
  899. * The name of the method to extend.
  900. *
  901. * @param {Highcharts.WrapProceedFunction} func
  902. * A wrapper function callback. This function is called with the same
  903. * arguments as the original function, except that the original function
  904. * is unshifted and passed as the first argument.
  905. */
  906. function wrap(obj, method, func) {
  907. const proceed = obj[method];
  908. obj[method] = function () {
  909. const outerArgs = arguments, scope = this;
  910. return func.apply(this, [
  911. function () {
  912. return proceed.apply(scope, arguments.length ? arguments : outerArgs);
  913. }
  914. ].concat([].slice.call(arguments)));
  915. };
  916. }
  917. /**
  918. * Get the magnitude of a number.
  919. *
  920. * @function Highcharts.getMagnitude
  921. *
  922. * @param {number} num
  923. * The number.
  924. *
  925. * @return {number}
  926. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  927. */
  928. function getMagnitude(num) {
  929. return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
  930. }
  931. /**
  932. * Take an interval and normalize it to multiples of round numbers.
  933. *
  934. * @deprecated
  935. * @function Highcharts.normalizeTickInterval
  936. *
  937. * @param {number} interval
  938. * The raw, un-rounded interval.
  939. *
  940. * @param {Array<*>} [multiples]
  941. * Allowed multiples.
  942. *
  943. * @param {number} [magnitude]
  944. * The magnitude of the number.
  945. *
  946. * @param {boolean} [allowDecimals]
  947. * Whether to allow decimals.
  948. *
  949. * @param {boolean} [hasTickAmount]
  950. * If it has tickAmount, avoid landing on tick intervals lower than
  951. * original.
  952. *
  953. * @return {number}
  954. * The normalized interval.
  955. *
  956. * @todo
  957. * Move this function to the Axis prototype. It is here only for historical
  958. * reasons.
  959. */
  960. function normalizeTickInterval(interval, multiples, magnitude, allowDecimals, hasTickAmount) {
  961. let i, retInterval = interval;
  962. // round to a tenfold of 1, 2, 2.5 or 5
  963. magnitude = pick(magnitude, getMagnitude(interval));
  964. const normalized = interval / magnitude;
  965. // multiples for a linear scale
  966. if (!multiples) {
  967. multiples = hasTickAmount ?
  968. // Finer grained ticks when the tick amount is hard set, including
  969. // when alignTicks is true on multiple axes (#4580).
  970. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  971. // Else, let ticks fall on rounder numbers
  972. [1, 2, 2.5, 5, 10];
  973. // the allowDecimals option
  974. if (allowDecimals === false) {
  975. if (magnitude === 1) {
  976. multiples = multiples.filter(function (num) {
  977. return num % 1 === 0;
  978. });
  979. }
  980. else if (magnitude <= 0.1) {
  981. multiples = [1 / magnitude];
  982. }
  983. }
  984. }
  985. // normalize the interval to the nearest multiple
  986. for (i = 0; i < multiples.length; i++) {
  987. retInterval = multiples[i];
  988. // only allow tick amounts smaller than natural
  989. if ((hasTickAmount &&
  990. retInterval * magnitude >= interval) ||
  991. (!hasTickAmount &&
  992. (normalized <=
  993. (multiples[i] +
  994. (multiples[i + 1] || multiples[i])) / 2))) {
  995. break;
  996. }
  997. }
  998. // Multiply back to the correct magnitude. Correct floats to appropriate
  999. // precision (#6085).
  1000. retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
  1001. return retInterval;
  1002. }
  1003. /**
  1004. * Sort an object array and keep the order of equal items. The ECMAScript
  1005. * standard does not specify the behaviour when items are equal.
  1006. *
  1007. * @function Highcharts.stableSort
  1008. *
  1009. * @param {Array<*>} arr
  1010. * The array to sort.
  1011. *
  1012. * @param {Function} sortFunction
  1013. * The function to sort it with, like with regular Array.prototype.sort.
  1014. */
  1015. function stableSort(arr, sortFunction) {
  1016. // @todo It seems like Chrome since v70 sorts in a stable way internally,
  1017. // plus all other browsers do it, so over time we may be able to remove this
  1018. // function
  1019. const length = arr.length;
  1020. let sortValue, i;
  1021. // Add index to each item
  1022. for (i = 0; i < length; i++) {
  1023. arr[i].safeI = i; // stable sort index
  1024. }
  1025. arr.sort(function (a, b) {
  1026. sortValue = sortFunction(a, b);
  1027. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1028. });
  1029. // Remove index from items
  1030. for (i = 0; i < length; i++) {
  1031. delete arr[i].safeI; // stable sort index
  1032. }
  1033. }
  1034. /**
  1035. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1036. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1037. * than 150.000 points. This method is slightly slower, but safe.
  1038. *
  1039. * @function Highcharts.arrayMin
  1040. *
  1041. * @param {Array<*>} data
  1042. * An array of numbers.
  1043. *
  1044. * @return {number}
  1045. * The lowest number.
  1046. */
  1047. function arrayMin(data) {
  1048. let i = data.length, min = data[0];
  1049. while (i--) {
  1050. if (data[i] < min) {
  1051. min = data[i];
  1052. }
  1053. }
  1054. return min;
  1055. }
  1056. /**
  1057. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1058. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1059. * than 150.000 points. This method is slightly slower, but safe.
  1060. *
  1061. * @function Highcharts.arrayMax
  1062. *
  1063. * @param {Array<*>} data
  1064. * An array of numbers.
  1065. *
  1066. * @return {number}
  1067. * The highest number.
  1068. */
  1069. function arrayMax(data) {
  1070. let i = data.length, max = data[0];
  1071. while (i--) {
  1072. if (data[i] > max) {
  1073. max = data[i];
  1074. }
  1075. }
  1076. return max;
  1077. }
  1078. /**
  1079. * Utility method that destroys any SVGElement instances that are properties on
  1080. * the given object. It loops all properties and invokes destroy if there is a
  1081. * destroy method. The property is then delete.
  1082. *
  1083. * @function Highcharts.destroyObjectProperties
  1084. *
  1085. * @param {*} obj
  1086. * The object to destroy properties on.
  1087. *
  1088. * @param {*} [except]
  1089. * Exception, do not destroy this property, only delete it.
  1090. */
  1091. function destroyObjectProperties(obj, except) {
  1092. objectEach(obj, function (val, n) {
  1093. // If the object is non-null and destroy is defined
  1094. if (val && val !== except && val.destroy) {
  1095. // Invoke the destroy
  1096. val.destroy();
  1097. }
  1098. // Delete the property from the object.
  1099. delete obj[n];
  1100. });
  1101. }
  1102. /**
  1103. * Discard a HTML element
  1104. *
  1105. * @function Highcharts.discardElement
  1106. *
  1107. * @param {Highcharts.HTMLDOMElement} element
  1108. * The HTML node to discard.
  1109. */
  1110. function discardElement(element) {
  1111. if (element && element.parentElement) {
  1112. element.parentElement.removeChild(element);
  1113. }
  1114. }
  1115. /**
  1116. * Fix JS round off float errors.
  1117. *
  1118. * @function Highcharts.correctFloat
  1119. *
  1120. * @param {number} num
  1121. * A float number to fix.
  1122. *
  1123. * @param {number} [prec=14]
  1124. * The precision.
  1125. *
  1126. * @return {number}
  1127. * The corrected float number.
  1128. */
  1129. function correctFloat(num, prec) {
  1130. // When the number is higher than 1e14 use the number (#16275)
  1131. return num > 1e14 ? num : parseFloat(num.toPrecision(prec || 14));
  1132. }
  1133. /**
  1134. * The time unit lookup
  1135. *
  1136. * @ignore
  1137. */
  1138. const timeUnits = {
  1139. millisecond: 1,
  1140. second: 1000,
  1141. minute: 60000,
  1142. hour: 3600000,
  1143. day: 24 * 3600000,
  1144. week: 7 * 24 * 3600000,
  1145. month: 28 * 24 * 3600000,
  1146. year: 364 * 24 * 3600000
  1147. };
  1148. /**
  1149. * Easing definition
  1150. *
  1151. * @private
  1152. * @function Math.easeInOutSine
  1153. *
  1154. * @param {number} pos
  1155. * Current position, ranging from 0 to 1.
  1156. *
  1157. * @return {number}
  1158. * Ease result
  1159. */
  1160. Math.easeInOutSine = function (pos) {
  1161. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1162. };
  1163. /**
  1164. * Find the closest distance between two values of a two-dimensional array
  1165. * @private
  1166. * @function Highcharts.getClosestDistance
  1167. *
  1168. * @param {Array<Array<number>>} arrays
  1169. * An array of arrays of numbers
  1170. *
  1171. * @return {number | undefined}
  1172. * The closest distance between values
  1173. */
  1174. function getClosestDistance(arrays, onError) {
  1175. const allowNegative = !onError;
  1176. let closest, loopLength, distance, i;
  1177. arrays.forEach((xData) => {
  1178. if (xData.length > 1) {
  1179. loopLength = xData.length - 1;
  1180. for (i = loopLength; i > 0; i--) {
  1181. distance = xData[i] - xData[i - 1];
  1182. if (distance < 0 && !allowNegative) {
  1183. onError === null || onError === void 0 ? void 0 : onError();
  1184. // Only one call
  1185. onError = void 0;
  1186. }
  1187. else if (distance && (typeof closest === 'undefined' || distance < closest)) {
  1188. closest = distance;
  1189. }
  1190. }
  1191. }
  1192. });
  1193. return closest;
  1194. }
  1195. /**
  1196. * Returns the value of a property path on a given object.
  1197. *
  1198. * @private
  1199. * @function getNestedProperty
  1200. *
  1201. * @param {string} path
  1202. * Path to the property, for example `custom.myValue`.
  1203. *
  1204. * @param {unknown} obj
  1205. * Instance containing the property on the specific path.
  1206. *
  1207. * @return {unknown}
  1208. * The unknown property value.
  1209. */
  1210. function getNestedProperty(path, parent) {
  1211. const pathElements = path.split('.');
  1212. while (pathElements.length && defined(parent)) {
  1213. const pathElement = pathElements.shift();
  1214. // Filter on the key
  1215. if (typeof pathElement === 'undefined' ||
  1216. pathElement === '__proto__') {
  1217. return; // undefined
  1218. }
  1219. if (pathElement === 'this') {
  1220. let thisProp;
  1221. if (isObject(parent)) {
  1222. thisProp = parent['@this'];
  1223. }
  1224. return thisProp !== null && thisProp !== void 0 ? thisProp : parent;
  1225. }
  1226. const child = parent[pathElement];
  1227. // Filter on the child
  1228. if (!defined(child) ||
  1229. typeof child === 'function' ||
  1230. typeof child.nodeType === 'number' ||
  1231. child === win) {
  1232. return; // undefined
  1233. }
  1234. // Else, proceed
  1235. parent = child;
  1236. }
  1237. return parent;
  1238. }
  1239. /**
  1240. * Get the computed CSS value for given element and property, only for numerical
  1241. * properties. For width and height, the dimension of the inner box (excluding
  1242. * padding) is returned. Used for fitting the chart within the container.
  1243. *
  1244. * @function Highcharts.getStyle
  1245. *
  1246. * @param {Highcharts.HTMLDOMElement} el
  1247. * An HTML element.
  1248. *
  1249. * @param {string} prop
  1250. * The property name.
  1251. *
  1252. * @param {boolean} [toInt=true]
  1253. * Parse to integer.
  1254. *
  1255. * @return {number|string|undefined}
  1256. * The style value.
  1257. */
  1258. function getStyle(el, prop, toInt) {
  1259. let style;
  1260. // For width and height, return the actual inner pixel size (#4913)
  1261. if (prop === 'width') {
  1262. let offsetWidth = Math.min(el.offsetWidth, el.scrollWidth);
  1263. // In flex boxes, we need to use getBoundingClientRect and floor it,
  1264. // because scrollWidth doesn't support subpixel precision (#6427) ...
  1265. const boundingClientRectWidth = el.getBoundingClientRect &&
  1266. el.getBoundingClientRect().width;
  1267. // ...unless if the containing div or its parents are transform-scaled
  1268. // down, in which case the boundingClientRect can't be used as it is
  1269. // also scaled down (#9871, #10498).
  1270. if (boundingClientRectWidth < offsetWidth &&
  1271. boundingClientRectWidth >= offsetWidth - 1) {
  1272. offsetWidth = Math.floor(boundingClientRectWidth);
  1273. }
  1274. return Math.max(0, // #8377
  1275. (offsetWidth -
  1276. (getStyle(el, 'padding-left', true) || 0) -
  1277. (getStyle(el, 'padding-right', true) || 0)));
  1278. }
  1279. if (prop === 'height') {
  1280. return Math.max(0, // #8377
  1281. (Math.min(el.offsetHeight, el.scrollHeight) -
  1282. (getStyle(el, 'padding-top', true) || 0) -
  1283. (getStyle(el, 'padding-bottom', true) || 0)));
  1284. }
  1285. // Otherwise, get the computed style
  1286. const css = win.getComputedStyle(el, void 0); // eslint-disable-line no-undefined
  1287. if (css) {
  1288. style = css.getPropertyValue(prop);
  1289. if (pick(toInt, prop !== 'opacity')) {
  1290. style = pInt(style);
  1291. }
  1292. }
  1293. return style;
  1294. }
  1295. /**
  1296. * Search for an item in an array.
  1297. *
  1298. * @function Highcharts.inArray
  1299. *
  1300. * @deprecated
  1301. *
  1302. * @param {*} item
  1303. * The item to search for.
  1304. *
  1305. * @param {Array<*>} arr
  1306. * The array or node collection to search in.
  1307. *
  1308. * @param {number} [fromIndex=0]
  1309. * The index to start searching from.
  1310. *
  1311. * @return {number}
  1312. * The index within the array, or -1 if not found.
  1313. */
  1314. function inArray(item, arr, fromIndex) {
  1315. error(32, false, void 0, { 'Highcharts.inArray': 'use Array.indexOf' });
  1316. return arr.indexOf(item, fromIndex);
  1317. }
  1318. /**
  1319. * Return the value of the first element in the array that satisfies the
  1320. * provided testing function.
  1321. *
  1322. * @function Highcharts.find<T>
  1323. *
  1324. * @param {Array<T>} arr
  1325. * The array to test.
  1326. *
  1327. * @param {Function} callback
  1328. * The callback function. The function receives the item as the first
  1329. * argument. Return `true` if this item satisfies the condition.
  1330. *
  1331. * @return {T|undefined}
  1332. * The value of the element.
  1333. */
  1334. const find = Array.prototype.find ?
  1335. function (arr, callback) {
  1336. return arr.find(callback);
  1337. } :
  1338. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  1339. function (arr, callback) {
  1340. let i;
  1341. const length = arr.length;
  1342. for (i = 0; i < length; i++) {
  1343. if (callback(arr[i], i)) { // eslint-disable-line node/callback-return
  1344. return arr[i];
  1345. }
  1346. }
  1347. };
  1348. /**
  1349. * Returns an array of a given object's own properties.
  1350. *
  1351. * @function Highcharts.keys
  1352. * @deprecated
  1353. *
  1354. * @param {*} obj
  1355. * The object of which the properties are to be returned.
  1356. *
  1357. * @return {Array<string>}
  1358. * An array of strings that represents all the properties.
  1359. */
  1360. function keys(obj) {
  1361. error(32, false, void 0, { 'Highcharts.keys': 'use Object.keys' });
  1362. return Object.keys(obj);
  1363. }
  1364. /**
  1365. * Get the element's offset position, corrected for `overflow: auto`.
  1366. *
  1367. * @function Highcharts.offset
  1368. *
  1369. * @param {global.Element} el
  1370. * The DOM element.
  1371. *
  1372. * @return {Highcharts.OffsetObject}
  1373. * An object containing `left` and `top` properties for the position in
  1374. * the page.
  1375. */
  1376. function offset(el) {
  1377. const docElem = doc.documentElement, box = (el.parentElement || el.parentNode) ?
  1378. el.getBoundingClientRect() :
  1379. { top: 0, left: 0, width: 0, height: 0 };
  1380. return {
  1381. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  1382. (docElem.clientTop || 0),
  1383. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  1384. (docElem.clientLeft || 0),
  1385. width: box.width,
  1386. height: box.height
  1387. };
  1388. }
  1389. /* eslint-disable valid-jsdoc */
  1390. /**
  1391. * Iterate over object key pairs in an object.
  1392. *
  1393. * @function Highcharts.objectEach<T>
  1394. *
  1395. * @param {*} obj
  1396. * The object to iterate over.
  1397. *
  1398. * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
  1399. * The iterator callback. It passes three arguments:
  1400. * * value - The property value.
  1401. * * key - The property key.
  1402. * * obj - The object that objectEach is being applied to.
  1403. *
  1404. * @param {T} [ctx]
  1405. * The context.
  1406. */
  1407. function objectEach(obj, fn, ctx) {
  1408. /* eslint-enable valid-jsdoc */
  1409. for (const key in obj) {
  1410. if (Object.hasOwnProperty.call(obj, key)) {
  1411. fn.call(ctx || obj[key], obj[key], key, obj);
  1412. }
  1413. }
  1414. }
  1415. /**
  1416. * Iterate over an array.
  1417. *
  1418. * @deprecated
  1419. * @function Highcharts.each
  1420. *
  1421. * @param {Array<*>} arr
  1422. * The array to iterate over.
  1423. *
  1424. * @param {Function} fn
  1425. * The iterator callback. It passes three arguments:
  1426. * - `item`: The array item.
  1427. * - `index`: The item's index in the array.
  1428. * - `arr`: The array that each is being applied to.
  1429. *
  1430. * @param {*} [ctx]
  1431. * The context.
  1432. *
  1433. * @return {void}
  1434. */
  1435. /**
  1436. * Filter an array by a callback.
  1437. *
  1438. * @deprecated
  1439. * @function Highcharts.grep
  1440. *
  1441. * @param {Array<*>} arr
  1442. * The array to filter.
  1443. *
  1444. * @param {Function} callback
  1445. * The callback function. The function receives the item as the first
  1446. * argument. Return `true` if the item is to be preserved.
  1447. *
  1448. * @return {Array<*>}
  1449. * A new, filtered array.
  1450. */
  1451. /**
  1452. * Map an array by a callback.
  1453. *
  1454. * @deprecated
  1455. * @function Highcharts.map
  1456. *
  1457. * @param {Array<*>} arr
  1458. * The array to map.
  1459. *
  1460. * @param {Function} fn
  1461. * The callback function. Return the new value for the new array.
  1462. *
  1463. * @return {Array<*>}
  1464. * A new array item with modified items.
  1465. */
  1466. /**
  1467. * Reduce an array to a single value.
  1468. *
  1469. * @deprecated
  1470. * @function Highcharts.reduce
  1471. *
  1472. * @param {Array<*>} arr
  1473. * The array to reduce.
  1474. *
  1475. * @param {Function} fn
  1476. * The callback function. Return the reduced value. Receives 4
  1477. * arguments: Accumulated/reduced value, current value, current array
  1478. * index, and the array.
  1479. *
  1480. * @param {*} initialValue
  1481. * The initial value of the accumulator.
  1482. *
  1483. * @return {*}
  1484. * The reduced value.
  1485. */
  1486. /**
  1487. * Test whether at least one element in the array passes the test implemented by
  1488. * the provided function.
  1489. *
  1490. * @deprecated
  1491. * @function Highcharts.some
  1492. *
  1493. * @param {Array<*>} arr
  1494. * The array to test
  1495. *
  1496. * @param {Function} fn
  1497. * The function to run on each item. Return truty to pass the test.
  1498. * Receives arguments `currentValue`, `index` and `array`.
  1499. *
  1500. * @param {*} ctx
  1501. * The context.
  1502. *
  1503. * @return {boolean}
  1504. */
  1505. objectEach({
  1506. map: 'map',
  1507. each: 'forEach',
  1508. grep: 'filter',
  1509. reduce: 'reduce',
  1510. some: 'some'
  1511. }, function (val, key) {
  1512. H[key] = function (arr) {
  1513. error(32, false, void 0, { [`Highcharts.${key}`]: `use Array.${val}` });
  1514. return Array.prototype[val].apply(arr, [].slice.call(arguments, 1));
  1515. };
  1516. });
  1517. /* eslint-disable valid-jsdoc */
  1518. /**
  1519. * Add an event listener.
  1520. *
  1521. * @function Highcharts.addEvent<T>
  1522. *
  1523. * @param {Highcharts.Class<T>|T} el
  1524. * The element or object to add a listener to. It can be a
  1525. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  1526. *
  1527. * @param {string} type
  1528. * The event type.
  1529. *
  1530. * @param {Highcharts.EventCallbackFunction<T>|Function} fn
  1531. * The function callback to execute when the event is fired.
  1532. *
  1533. * @param {Highcharts.EventOptionsObject} [options]
  1534. * Options for adding the event.
  1535. *
  1536. * @return {Function}
  1537. * A callback function to remove the added event.
  1538. */
  1539. function addEvent(el, type, fn, options = {}) {
  1540. /* eslint-enable valid-jsdoc */
  1541. // Add hcEvents to either the prototype (in case we're running addEvent on a
  1542. // class) or the instance. If hasOwnProperty('hcEvents') is false, it is
  1543. // inherited down the prototype chain, in which case we need to set the
  1544. // property on this instance (which may itself be a prototype).
  1545. const owner = typeof el === 'function' && el.prototype || el;
  1546. if (!Object.hasOwnProperty.call(owner, 'hcEvents')) {
  1547. owner.hcEvents = {};
  1548. }
  1549. const events = owner.hcEvents;
  1550. // Allow click events added to points, otherwise they will be prevented by
  1551. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  1552. if (H.Point && // without H a dependency loop occurs
  1553. el instanceof H.Point &&
  1554. el.series &&
  1555. el.series.chart) {
  1556. el.series.chart.runTrackerClick = true;
  1557. }
  1558. // Handle DOM events
  1559. // If the browser supports passive events, add it to improve performance
  1560. // on touch events (#11353).
  1561. const addEventListener = el.addEventListener;
  1562. if (addEventListener) {
  1563. addEventListener.call(el, type, fn, H.supportsPassiveEvents ? {
  1564. passive: options.passive === void 0 ?
  1565. type.indexOf('touch') !== -1 : options.passive,
  1566. capture: false
  1567. } : false);
  1568. }
  1569. if (!events[type]) {
  1570. events[type] = [];
  1571. }
  1572. const eventObject = {
  1573. fn,
  1574. order: typeof options.order === 'number' ? options.order : Infinity
  1575. };
  1576. events[type].push(eventObject);
  1577. // Order the calls
  1578. events[type].sort((a, b) => a.order - b.order);
  1579. // Return a function that can be called to remove this event.
  1580. return function () {
  1581. removeEvent(el, type, fn);
  1582. };
  1583. }
  1584. /* eslint-disable valid-jsdoc */
  1585. /**
  1586. * Remove an event that was added with {@link Highcharts#addEvent}.
  1587. *
  1588. * @function Highcharts.removeEvent<T>
  1589. *
  1590. * @param {Highcharts.Class<T>|T} el
  1591. * The element to remove events on.
  1592. *
  1593. * @param {string} [type]
  1594. * The type of events to remove. If undefined, all events are removed
  1595. * from the element.
  1596. *
  1597. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  1598. * The specific callback to remove. If undefined, all events that match
  1599. * the element and optionally the type are removed.
  1600. *
  1601. * @return {void}
  1602. */
  1603. function removeEvent(el, type, fn) {
  1604. /* eslint-enable valid-jsdoc */
  1605. /**
  1606. * @private
  1607. */
  1608. function removeOneEvent(type, fn) {
  1609. const removeEventListener = el.removeEventListener;
  1610. if (removeEventListener) {
  1611. removeEventListener.call(el, type, fn, false);
  1612. }
  1613. }
  1614. /**
  1615. * @private
  1616. */
  1617. function removeAllEvents(eventCollection) {
  1618. let types, len;
  1619. if (!el.nodeName) {
  1620. return; // break on non-DOM events
  1621. }
  1622. if (type) {
  1623. types = {};
  1624. types[type] = true;
  1625. }
  1626. else {
  1627. types = eventCollection;
  1628. }
  1629. objectEach(types, function (_val, n) {
  1630. if (eventCollection[n]) {
  1631. len = eventCollection[n].length;
  1632. while (len--) {
  1633. removeOneEvent(n, eventCollection[n][len].fn);
  1634. }
  1635. }
  1636. });
  1637. }
  1638. const owner = typeof el === 'function' && el.prototype || el;
  1639. if (Object.hasOwnProperty.call(owner, 'hcEvents')) {
  1640. const events = owner.hcEvents;
  1641. if (type) {
  1642. const typeEvents = (events[type] || []);
  1643. if (fn) {
  1644. events[type] = typeEvents.filter(function (obj) {
  1645. return fn !== obj.fn;
  1646. });
  1647. removeOneEvent(type, fn);
  1648. }
  1649. else {
  1650. removeAllEvents(events);
  1651. events[type] = [];
  1652. }
  1653. }
  1654. else {
  1655. removeAllEvents(events);
  1656. delete owner.hcEvents;
  1657. }
  1658. }
  1659. }
  1660. /* eslint-disable valid-jsdoc */
  1661. /**
  1662. * Fire an event that was registered with {@link Highcharts#addEvent}.
  1663. *
  1664. * @function Highcharts.fireEvent<T>
  1665. *
  1666. * @param {T} el
  1667. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  1668. * an {@link SVGElement} or any other object.
  1669. *
  1670. * @param {string} type
  1671. * The type of event.
  1672. *
  1673. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  1674. * Custom event arguments that are passed on as an argument to the event
  1675. * handler.
  1676. *
  1677. * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
  1678. * The default function to execute if the other listeners haven't
  1679. * returned false.
  1680. *
  1681. * @return {void}
  1682. */
  1683. function fireEvent(el, type, eventArguments, defaultFunction) {
  1684. /* eslint-enable valid-jsdoc */
  1685. let e, i;
  1686. eventArguments = eventArguments || {};
  1687. if (doc.createEvent &&
  1688. (el.dispatchEvent ||
  1689. (el.fireEvent &&
  1690. // Enable firing events on Highcharts instance.
  1691. el !== H))) {
  1692. e = doc.createEvent('Events');
  1693. e.initEvent(type, true, true);
  1694. eventArguments = extend(e, eventArguments);
  1695. if (el.dispatchEvent) {
  1696. el.dispatchEvent(eventArguments);
  1697. }
  1698. else {
  1699. el.fireEvent(type, eventArguments);
  1700. }
  1701. }
  1702. else if (el.hcEvents) {
  1703. if (!eventArguments.target) {
  1704. // We're running a custom event
  1705. extend(eventArguments, {
  1706. // Attach a simple preventDefault function to skip
  1707. // default handler if called. The built-in
  1708. // defaultPrevented property is not overwritable (#5112)
  1709. preventDefault: function () {
  1710. eventArguments.defaultPrevented = true;
  1711. },
  1712. // Setting target to native events fails with clicking
  1713. // the zoom-out button in Chrome.
  1714. target: el,
  1715. // If the type is not set, we're running a custom event
  1716. // (#2297). If it is set, we're running a browser event.
  1717. type: type
  1718. });
  1719. }
  1720. const events = [];
  1721. let object = el;
  1722. let multilevel = false;
  1723. // Recurse up the inheritance chain and collect hcEvents set as own
  1724. // objects on the prototypes.
  1725. while (object.hcEvents) {
  1726. if (Object.hasOwnProperty.call(object, 'hcEvents') &&
  1727. object.hcEvents[type]) {
  1728. if (events.length) {
  1729. multilevel = true;
  1730. }
  1731. events.unshift.apply(events, object.hcEvents[type]);
  1732. }
  1733. object = Object.getPrototypeOf(object);
  1734. }
  1735. // For performance reasons, only sort the event handlers in case we are
  1736. // dealing with multiple levels in the prototype chain. Otherwise, the
  1737. // events are already sorted in the addEvent function.
  1738. if (multilevel) {
  1739. // Order the calls
  1740. events.sort((a, b) => a.order - b.order);
  1741. }
  1742. // Call the collected event handlers
  1743. events.forEach((obj) => {
  1744. // If the event handler returns false, prevent the default handler
  1745. // from executing
  1746. if (obj.fn.call(el, eventArguments) === false) {
  1747. eventArguments.preventDefault();
  1748. }
  1749. });
  1750. }
  1751. // Run the default if not prevented
  1752. if (defaultFunction && !eventArguments.defaultPrevented) {
  1753. defaultFunction.call(el, eventArguments);
  1754. }
  1755. }
  1756. let serialMode;
  1757. /**
  1758. * Get a unique key for using in internal element id's and pointers. The key is
  1759. * composed of a random hash specific to this Highcharts instance, and a
  1760. * counter.
  1761. *
  1762. * @example
  1763. * let id = uniqueKey(); // => 'highcharts-x45f6hp-0'
  1764. *
  1765. * @function Highcharts.uniqueKey
  1766. *
  1767. * @return {string}
  1768. * A unique key.
  1769. */
  1770. const uniqueKey = (function () {
  1771. const hash = Math.random().toString(36).substring(2, 9) + '-';
  1772. let id = 0;
  1773. return function () {
  1774. return 'highcharts-' + (serialMode ? '' : hash) + id++;
  1775. };
  1776. }());
  1777. /**
  1778. * Activates a serial mode for element IDs provided by
  1779. * {@link Highcharts.uniqueKey}. This mode can be used in automated tests, where
  1780. * a simple comparison of two rendered SVG graphics is needed.
  1781. *
  1782. * **Note:** This is only for testing purposes and will break functionality in
  1783. * webpages with multiple charts.
  1784. *
  1785. * @example
  1786. * if (
  1787. * process &&
  1788. * process.env.NODE_ENV === 'development'
  1789. * ) {
  1790. * Highcharts.useSerialIds(true);
  1791. * }
  1792. *
  1793. * @function Highcharts.useSerialIds
  1794. *
  1795. * @param {boolean} [mode]
  1796. * Changes the state of serial mode.
  1797. *
  1798. * @return {boolean|undefined}
  1799. * State of the serial mode.
  1800. */
  1801. function useSerialIds(mode) {
  1802. return (serialMode = pick(mode, serialMode));
  1803. }
  1804. function isFunction(obj) {
  1805. return typeof obj === 'function';
  1806. }
  1807. // Register Highcharts as a plugin in jQuery
  1808. if (win.jQuery) {
  1809. /**
  1810. * Highcharts-extended JQuery.
  1811. *
  1812. * @external JQuery
  1813. */
  1814. /**
  1815. * Helper function to return the chart of the current JQuery selector
  1816. * element.
  1817. *
  1818. * @function external:JQuery#highcharts
  1819. *
  1820. * @return {Highcharts.Chart}
  1821. * The chart that is linked to the JQuery selector element.
  1822. */ /**
  1823. * Factory function to create a chart in the current JQuery selector
  1824. * element.
  1825. *
  1826. * @function external:JQuery#highcharts
  1827. *
  1828. * @param {'Chart'|'Map'|'StockChart'|string} [className]
  1829. * Name of the factory class in the Highcharts namespace.
  1830. *
  1831. * @param {Highcharts.Options} [options]
  1832. * The chart options structure.
  1833. *
  1834. * @param {Highcharts.ChartCallbackFunction} [callback]
  1835. * Function to run when the chart has loaded and and all external
  1836. * images are loaded. Defining a
  1837. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  1838. * handler is equivalent.
  1839. *
  1840. * @return {JQuery}
  1841. * The current JQuery selector.
  1842. */
  1843. win.jQuery.fn.highcharts = function () {
  1844. const args = [].slice.call(arguments);
  1845. if (this[0]) { // this[0] is the renderTo div
  1846. // Create the chart
  1847. if (args[0]) {
  1848. new H[ // eslint-disable-line computed-property-spacing, no-new
  1849. // Constructor defaults to Chart
  1850. isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
  1851. return this;
  1852. }
  1853. // When called without parameters or with the return argument,
  1854. // return an existing chart
  1855. return charts[attr(this[0], 'data-highcharts-chart')];
  1856. }
  1857. };
  1858. }
  1859. /* *
  1860. *
  1861. * Default Export
  1862. *
  1863. * */
  1864. // TODO use named exports when supported.
  1865. const Utilities = {
  1866. addEvent,
  1867. arrayMax,
  1868. arrayMin,
  1869. attr,
  1870. clamp,
  1871. clearTimeout: internalClearTimeout,
  1872. correctFloat,
  1873. createElement,
  1874. css,
  1875. defined,
  1876. destroyObjectProperties,
  1877. diffObjects,
  1878. discardElement,
  1879. erase,
  1880. error,
  1881. extend,
  1882. extendClass,
  1883. find,
  1884. fireEvent,
  1885. getClosestDistance,
  1886. getMagnitude,
  1887. getNestedProperty,
  1888. getStyle,
  1889. inArray,
  1890. insertItem,
  1891. isArray,
  1892. isClass,
  1893. isDOMElement,
  1894. isFunction,
  1895. isNumber,
  1896. isObject,
  1897. isString,
  1898. keys,
  1899. merge,
  1900. normalizeTickInterval,
  1901. objectEach,
  1902. offset,
  1903. pad,
  1904. pick,
  1905. pInt,
  1906. pushUnique,
  1907. relativeLength,
  1908. removeEvent,
  1909. splat,
  1910. stableSort,
  1911. syncTimeout,
  1912. timeUnits,
  1913. uniqueKey,
  1914. useSerialIds,
  1915. wrap
  1916. };
  1917. /* *
  1918. *
  1919. * API Declarations
  1920. *
  1921. * */
  1922. /**
  1923. * An animation configuration. Animation configurations can also be defined as
  1924. * booleans, where `false` turns off animation and `true` defaults to a duration
  1925. * of 500ms and defer of 0ms.
  1926. *
  1927. * @interface Highcharts.AnimationOptionsObject
  1928. */ /**
  1929. * A callback function to exectute when the animation finishes.
  1930. * @name Highcharts.AnimationOptionsObject#complete
  1931. * @type {Function|undefined}
  1932. */ /**
  1933. * The animation defer in milliseconds.
  1934. * @name Highcharts.AnimationOptionsObject#defer
  1935. * @type {number|undefined}
  1936. */ /**
  1937. * The animation duration in milliseconds.
  1938. * @name Highcharts.AnimationOptionsObject#duration
  1939. * @type {number|undefined}
  1940. */ /**
  1941. * The name of an easing function as defined on the `Math` object.
  1942. * @name Highcharts.AnimationOptionsObject#easing
  1943. * @type {string|Function|undefined}
  1944. */ /**
  1945. * A callback function to execute on each step of each attribute or CSS property
  1946. * that's being animated. The first argument contains information about the
  1947. * animation and progress.
  1948. * @name Highcharts.AnimationOptionsObject#step
  1949. * @type {Function|undefined}
  1950. */
  1951. /**
  1952. * Creates a frame for the animated SVG element.
  1953. *
  1954. * @callback Highcharts.AnimationStepCallbackFunction
  1955. *
  1956. * @param {Highcharts.SVGElement} this
  1957. * The SVG element to animate.
  1958. *
  1959. * @return {void}
  1960. */
  1961. /**
  1962. * Interface description for a class.
  1963. *
  1964. * @interface Highcharts.Class<T>
  1965. * @extends Function
  1966. */ /**
  1967. * Class costructor.
  1968. * @function Highcharts.Class<T>#new
  1969. * @param {...Array<*>} args
  1970. * Constructor arguments.
  1971. * @return {T}
  1972. * Class instance.
  1973. */
  1974. /**
  1975. * A style object with camel case property names to define visual appearance of
  1976. * a SVG element or HTML element. The properties can be whatever styles are
  1977. * supported on the given SVG or HTML element.
  1978. *
  1979. * @example
  1980. * {
  1981. * fontFamily: 'monospace',
  1982. * fontSize: '1.2em'
  1983. * }
  1984. *
  1985. * @interface Highcharts.CSSObject
  1986. */ /**
  1987. * @name Highcharts.CSSObject#[key:string]
  1988. * @type {boolean|number|string|undefined}
  1989. */ /**
  1990. * Background style for the element.
  1991. * @name Highcharts.CSSObject#background
  1992. * @type {string|undefined}
  1993. */ /**
  1994. * Background color of the element.
  1995. * @name Highcharts.CSSObject#backgroundColor
  1996. * @type {Highcharts.ColorString|undefined}
  1997. */ /**
  1998. * Border style for the element.
  1999. * @name Highcharts.CSSObject#border
  2000. * @type {string|undefined}
  2001. */ /**
  2002. * Radius of the element border.
  2003. * @name Highcharts.CSSObject#borderRadius
  2004. * @type {number|undefined}
  2005. */ /**
  2006. * Color used in the element. The 'contrast' option is a Highcharts custom
  2007. * property that results in black or white, depending on the background of the
  2008. * element.
  2009. * @name Highcharts.CSSObject#color
  2010. * @type {'contrast'|Highcharts.ColorString|undefined}
  2011. */ /**
  2012. * Style of the mouse cursor when resting over the element.
  2013. * @name Highcharts.CSSObject#cursor
  2014. * @type {Highcharts.CursorValue|undefined}
  2015. */ /**
  2016. * Font family of the element text. Multiple values have to be in decreasing
  2017. * preference order and separated by comma.
  2018. * @name Highcharts.CSSObject#fontFamily
  2019. * @type {string|undefined}
  2020. */ /**
  2021. * Font size of the element text.
  2022. * @name Highcharts.CSSObject#fontSize
  2023. * @type {string|undefined}
  2024. */ /**
  2025. * Font weight of the element text.
  2026. * @name Highcharts.CSSObject#fontWeight
  2027. * @type {string|undefined}
  2028. */ /**
  2029. * Height of the element.
  2030. * @name Highcharts.CSSObject#height
  2031. * @type {number|undefined}
  2032. */ /**
  2033. * Width of the element border.
  2034. * @name Highcharts.CSSObject#lineWidth
  2035. * @type {number|undefined}
  2036. */ /**
  2037. * Opacity of the element.
  2038. * @name Highcharts.CSSObject#opacity
  2039. * @type {number|undefined}
  2040. */ /**
  2041. * Space around the element content.
  2042. * @name Highcharts.CSSObject#padding
  2043. * @type {string|undefined}
  2044. */ /**
  2045. * Behaviour of the element when the mouse cursor rests over it.
  2046. * @name Highcharts.CSSObject#pointerEvents
  2047. * @type {string|undefined}
  2048. */ /**
  2049. * Positioning of the element.
  2050. * @name Highcharts.CSSObject#position
  2051. * @type {string|undefined}
  2052. */ /**
  2053. * Alignment of the element text.
  2054. * @name Highcharts.CSSObject#textAlign
  2055. * @type {string|undefined}
  2056. */ /**
  2057. * Additional decoration of the element text.
  2058. * @name Highcharts.CSSObject#textDecoration
  2059. * @type {string|undefined}
  2060. */ /**
  2061. * Outline style of the element text.
  2062. * @name Highcharts.CSSObject#textOutline
  2063. * @type {string|undefined}
  2064. */ /**
  2065. * Line break style of the element text. Highcharts SVG elements support
  2066. * `ellipsis` when a `width` is set.
  2067. * @name Highcharts.CSSObject#textOverflow
  2068. * @type {string|undefined}
  2069. */ /**
  2070. * Top spacing of the element relative to the parent element.
  2071. * @name Highcharts.CSSObject#top
  2072. * @type {string|undefined}
  2073. */ /**
  2074. * Animated transition of selected element properties.
  2075. * @name Highcharts.CSSObject#transition
  2076. * @type {string|undefined}
  2077. */ /**
  2078. * Line break style of the element text.
  2079. * @name Highcharts.CSSObject#whiteSpace
  2080. * @type {string|undefined}
  2081. */ /**
  2082. * Width of the element.
  2083. * @name Highcharts.CSSObject#width
  2084. * @type {number|undefined}
  2085. */
  2086. /**
  2087. * All possible cursor styles.
  2088. *
  2089. * @typedef {'alias'|'all-scroll'|'auto'|'cell'|'col-resize'|'context-menu'|'copy'|'crosshair'|'default'|'e-resize'|'ew-resize'|'grab'|'grabbing'|'help'|'move'|'n-resize'|'ne-resize'|'nesw-resize'|'no-drop'|'none'|'not-allowed'|'ns-resize'|'nw-resize'|'nwse-resize'|'pointer'|'progress'|'row-resize'|'s-resize'|'se-resize'|'sw-resize'|'text'|'vertical-text'|'w-resize'|'wait'|'zoom-in'|'zoom-out'} Highcharts.CursorValue
  2090. */
  2091. /**
  2092. * All possible dash styles.
  2093. *
  2094. * @typedef {'Dash'|'DashDot'|'Dot'|'LongDash'|'LongDashDot'|'LongDashDotDot'|'ShortDash'|'ShortDashDot'|'ShortDashDotDot'|'ShortDot'|'Solid'} Highcharts.DashStyleValue
  2095. */
  2096. /**
  2097. * Generic dictionary in TypeScript notation.
  2098. * Use the native `AnyRecord` instead.
  2099. *
  2100. * @deprecated
  2101. * @interface Highcharts.Dictionary<T>
  2102. */ /**
  2103. * @name Highcharts.Dictionary<T>#[key:string]
  2104. * @type {T}
  2105. */
  2106. /**
  2107. * The function callback to execute when the event is fired. The `this` context
  2108. * contains the instance, that fired the event.
  2109. *
  2110. * @callback Highcharts.EventCallbackFunction<T>
  2111. *
  2112. * @param {T} this
  2113. *
  2114. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  2115. * Event arguments.
  2116. *
  2117. * @return {boolean|void}
  2118. */
  2119. /**
  2120. * The event options for adding function callback.
  2121. *
  2122. * @interface Highcharts.EventOptionsObject
  2123. */ /**
  2124. * The order the event handler should be called. This opens for having one
  2125. * handler be called before another, independent of in which order they were
  2126. * added.
  2127. * @name Highcharts.EventOptionsObject#order
  2128. * @type {number}
  2129. */ /**
  2130. * Whether an event should be passive or not.
  2131. * When set to `true`, the function specified by listener will never call
  2132. * `preventDefault()`.
  2133. * @name Highcharts.EventOptionsObject#passive
  2134. * @type boolean
  2135. */
  2136. /**
  2137. * Formats data as a string. Usually the data is accessible throught the `this`
  2138. * keyword.
  2139. *
  2140. * @callback Highcharts.FormatterCallbackFunction<T>
  2141. *
  2142. * @param {T} this
  2143. * Context to format
  2144. *
  2145. * @return {string}
  2146. * Formatted text
  2147. */
  2148. /**
  2149. * An object of key-value pairs for HTML attributes.
  2150. *
  2151. * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
  2152. */
  2153. /**
  2154. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  2155. * the global scope.
  2156. *
  2157. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  2158. *
  2159. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  2160. */
  2161. /**
  2162. * The iterator callback.
  2163. *
  2164. * @callback Highcharts.ObjectEachCallbackFunction<T>
  2165. *
  2166. * @param {T} this
  2167. * The context.
  2168. *
  2169. * @param {*} value
  2170. * The property value.
  2171. *
  2172. * @param {string} key
  2173. * The property key.
  2174. *
  2175. * @param {*} obj
  2176. * The object that objectEach is being applied to.
  2177. */
  2178. /**
  2179. * An object containing `left` and `top` properties for the position in the
  2180. * page.
  2181. *
  2182. * @interface Highcharts.OffsetObject
  2183. */ /**
  2184. * Left distance to the page border.
  2185. * @name Highcharts.OffsetObject#left
  2186. * @type {number}
  2187. */ /**
  2188. * Top distance to the page border.
  2189. * @name Highcharts.OffsetObject#top
  2190. * @type {number}
  2191. */
  2192. /**
  2193. * Describes a range.
  2194. *
  2195. * @interface Highcharts.RangeObject
  2196. */ /**
  2197. * Maximum number of the range.
  2198. * @name Highcharts.RangeObject#max
  2199. * @type {number}
  2200. */ /**
  2201. * Minimum number of the range.
  2202. * @name Highcharts.RangeObject#min
  2203. * @type {number}
  2204. */
  2205. /**
  2206. * If a number is given, it defines the pixel length. If a percentage string is
  2207. * given, like for example `'50%'`, the setting defines a length relative to a
  2208. * base size, for example the size of a container.
  2209. *
  2210. * @typedef {number|string} Highcharts.RelativeSize
  2211. */
  2212. /**
  2213. * Proceed function to call original (wrapped) function.
  2214. *
  2215. * @callback Highcharts.WrapProceedFunction
  2216. *
  2217. * @param {*} [arg1]
  2218. * Optional argument. Without any arguments defaults to first argument of
  2219. * the wrapping function.
  2220. *
  2221. * @param {*} [arg2]
  2222. * Optional argument. Without any arguments defaults to second argument
  2223. * of the wrapping function.
  2224. *
  2225. * @param {*} [arg3]
  2226. * Optional argument. Without any arguments defaults to third argument of
  2227. * the wrapping function.
  2228. *
  2229. * @return {*}
  2230. * Return value of the original function.
  2231. */
  2232. /**
  2233. * The Highcharts object is the placeholder for all other members, and various
  2234. * utility functions. The most important member of the namespace would be the
  2235. * chart constructor.
  2236. *
  2237. * @example
  2238. * let chart = Highcharts.chart('container', { ... });
  2239. *
  2240. * @namespace Highcharts
  2241. */
  2242. ''; // detach doclets above
  2243. return Utilities;
  2244. });
  2245. _registerModule(_modules, 'Core/Chart/ChartDefaults.js', [], function () {
  2246. /* *
  2247. *
  2248. * (c) 2010-2021 Torstein Honsi
  2249. *
  2250. * License: www.highcharts.com/license
  2251. *
  2252. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2253. *
  2254. * */
  2255. /* *
  2256. *
  2257. * API Options
  2258. *
  2259. * */
  2260. /**
  2261. * General options for the chart.
  2262. *
  2263. * @optionparent chart
  2264. */
  2265. const ChartDefaults = {
  2266. /**
  2267. * Default `mapData` for all series, in terms of a GeoJSON or TopoJSON
  2268. * object. If set to a string, it functions as an index into the
  2269. * `Highcharts.maps` array.
  2270. *
  2271. * For picking out individual shapes and geometries to use for each series
  2272. * of the map, see [series.mapData](#series.map.mapData).
  2273. *
  2274. * @sample maps/demo/geojson
  2275. * Loading GeoJSON data
  2276. * @sample maps/chart/topojson
  2277. * Loading TopoJSON data
  2278. *
  2279. * @type {string|Array<*>|Highcharts.GeoJSON|Highcharts.TopoJSON}
  2280. * @since 5.0.0
  2281. * @product highmaps
  2282. * @apioption chart.map
  2283. */
  2284. /**
  2285. * Set lat/lon transformation definitions for the chart. If not defined,
  2286. * these are extracted from the map data.
  2287. *
  2288. * @type {*}
  2289. * @since 5.0.0
  2290. * @product highmaps
  2291. * @apioption chart.mapTransforms
  2292. */
  2293. /**
  2294. * When using multiple axes, the ticks of two or more opposite axes
  2295. * will automatically be aligned by adding ticks to the axis or axes
  2296. * with the least ticks, as if `tickAmount` were specified.
  2297. *
  2298. * This can be prevented by setting `alignTicks` to false. If the grid
  2299. * lines look messy, it's a good idea to hide them for the secondary
  2300. * axis by setting `gridLineWidth` to 0.
  2301. *
  2302. * If `startOnTick` or `endOnTick` in the axis options are set to false,
  2303. * then the `alignTicks ` will be disabled for the axis.
  2304. *
  2305. * Disabled for logarithmic axes.
  2306. *
  2307. * @sample {highcharts} highcharts/chart/alignticks-true/
  2308. * True by default
  2309. * @sample {highcharts} highcharts/chart/alignticks-false/
  2310. * False
  2311. * @sample {highstock} stock/chart/alignticks-true/
  2312. * True by default
  2313. * @sample {highstock} stock/chart/alignticks-false/
  2314. * False
  2315. *
  2316. * @type {boolean}
  2317. * @default true
  2318. * @product highcharts highstock gantt
  2319. * @apioption chart.alignTicks
  2320. */
  2321. /**
  2322. * When using multiple axes, align the thresholds. When this is true, other
  2323. * ticks will also be aligned.
  2324. *
  2325. * Note that for line series and some other series types, the `threshold`
  2326. * option is set to `null` by default. This will in turn cause their y-axis
  2327. * to not have a threshold. In order to avoid that, set the series
  2328. * `threshold` to 0 or another number.
  2329. *
  2330. * If `startOnTick` or `endOnTick` in the axis options are set to false, or
  2331. * if the axis is logarithmic, the threshold will not be aligned.
  2332. *
  2333. * @sample {highcharts} highcharts/chart/alignthresholds/ Set to true
  2334. *
  2335. * @since 10.0.0
  2336. * @product highcharts highstock gantt
  2337. * @apioption chart.alignThresholds
  2338. */
  2339. alignThresholds: false,
  2340. /**
  2341. * Set the overall animation for all chart updating. Animation can be
  2342. * disabled throughout the chart by setting it to false here. It can
  2343. * be overridden for each individual API method as a function parameter.
  2344. * The only animation not affected by this option is the initial series
  2345. * animation, see [plotOptions.series.animation](
  2346. * #plotOptions.series.animation).
  2347. *
  2348. * The animation can either be set as a boolean or a configuration
  2349. * object. If `true`, it will use the 'swing' jQuery easing and a
  2350. * duration of 500 ms. If used as a configuration object, the following
  2351. * properties are supported:
  2352. *
  2353. * - `defer`: The animation delay time in milliseconds.
  2354. *
  2355. * - `duration`: The duration of the animation in milliseconds.
  2356. *
  2357. * - `easing`: A string reference to an easing function set on the
  2358. * `Math` object. See
  2359. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  2360. *
  2361. * When zooming on a series with less than 100 points, the chart redraw
  2362. * will be done with animation, but in case of more data points, it is
  2363. * necessary to set this option to ensure animation on zoom.
  2364. *
  2365. * @sample {highcharts} highcharts/chart/animation-none/
  2366. * Updating with no animation
  2367. * @sample {highcharts} highcharts/chart/animation-duration/
  2368. * With a longer duration
  2369. * @sample {highcharts} highcharts/chart/animation-easing/
  2370. * With a jQuery UI easing
  2371. * @sample {highmaps} maps/chart/animation-none/
  2372. * Updating with no animation
  2373. * @sample {highmaps} maps/chart/animation-duration/
  2374. * With a longer duration
  2375. *
  2376. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  2377. * @default true
  2378. * @apioption chart.animation
  2379. */
  2380. /**
  2381. * A CSS class name to apply to the charts container `div`, allowing
  2382. * unique CSS styling for each chart.
  2383. *
  2384. * @type {string}
  2385. * @apioption chart.className
  2386. */
  2387. /**
  2388. * Event listeners for the chart.
  2389. *
  2390. * @apioption chart.events
  2391. */
  2392. /**
  2393. * Fires when a series is added to the chart after load time, using the
  2394. * `addSeries` method. One parameter, `event`, is passed to the
  2395. * function, containing common event information. Through
  2396. * `event.options` you can access the series options that were passed to
  2397. * the `addSeries` method. Returning false prevents the series from
  2398. * being added.
  2399. *
  2400. * @sample {highcharts} highcharts/chart/events-addseries/
  2401. * Alert on add series
  2402. * @sample {highstock} stock/chart/events-addseries/
  2403. * Alert on add series
  2404. *
  2405. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  2406. * @since 1.2.0
  2407. * @context Highcharts.Chart
  2408. * @apioption chart.events.addSeries
  2409. */
  2410. /**
  2411. * Fires when clicking on the plot background. One parameter, `event`,
  2412. * is passed to the function, containing common event information.
  2413. *
  2414. * Information on the clicked spot can be found through `event.xAxis`
  2415. * and `event.yAxis`, which are arrays containing the axes of each
  2416. * dimension and each axis' value at the clicked spot. The primary axes
  2417. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  2418. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  2419. *
  2420. * ```js
  2421. * click: function(e) {
  2422. * console.log(
  2423. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  2424. * e.yAxis[0].value
  2425. * )
  2426. * }
  2427. * ```
  2428. *
  2429. * @sample {highcharts} highcharts/chart/events-click/
  2430. * Alert coordinates on click
  2431. * @sample {highcharts} highcharts/chart/events-container/
  2432. * Alternatively, attach event to container
  2433. * @sample {highstock} stock/chart/events-click/
  2434. * Alert coordinates on click
  2435. * @sample {highstock} highcharts/chart/events-container/
  2436. * Alternatively, attach event to container
  2437. * @sample {highmaps} maps/chart/events-click/
  2438. * Record coordinates on click
  2439. * @sample {highmaps} highcharts/chart/events-container/
  2440. * Alternatively, attach event to container
  2441. *
  2442. * @type {Highcharts.ChartClickCallbackFunction}
  2443. * @since 1.2.0
  2444. * @context Highcharts.Chart
  2445. * @apioption chart.events.click
  2446. */
  2447. /**
  2448. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  2449. * for images to be loaded, for example from point markers. One
  2450. * parameter, `event`, is passed to the function, containing common
  2451. * event information.
  2452. *
  2453. * There is also a second parameter to the chart constructor where a
  2454. * callback function can be passed to be executed on chart.load.
  2455. *
  2456. * @sample {highcharts} highcharts/chart/events-load/
  2457. * Alert on chart load
  2458. * @sample {highcharts} highcharts/chart/events-render/
  2459. * Load vs Redraw vs Render
  2460. * @sample {highstock} stock/chart/events-load/
  2461. * Alert on chart load
  2462. * @sample {highmaps} maps/chart/events-load/
  2463. * Add series on chart load
  2464. *
  2465. * @type {Highcharts.ChartLoadCallbackFunction}
  2466. * @context Highcharts.Chart
  2467. * @apioption chart.events.load
  2468. */
  2469. /**
  2470. * Fires when the chart is redrawn, either after a call to
  2471. * `chart.redraw()` or after an axis, series or point is modified with
  2472. * the `redraw` option set to `true`. One parameter, `event`, is passed
  2473. * to the function, containing common event information.
  2474. *
  2475. * @sample {highcharts} highcharts/chart/events-redraw/
  2476. * Alert on chart redraw
  2477. * @sample {highcharts} highcharts/chart/events-render/
  2478. * Load vs Redraw vs Render
  2479. * @sample {highstock} stock/chart/events-redraw/
  2480. * Alert on chart redraw when adding a series or moving the
  2481. * zoomed range
  2482. * @sample {highmaps} maps/chart/events-redraw/
  2483. * Set subtitle on chart redraw
  2484. *
  2485. * @type {Highcharts.ChartRedrawCallbackFunction}
  2486. * @since 1.2.0
  2487. * @context Highcharts.Chart
  2488. * @apioption chart.events.redraw
  2489. */
  2490. /**
  2491. * Fires after initial load of the chart (directly after the `load`
  2492. * event), and after each redraw (directly after the `redraw` event).
  2493. *
  2494. * @sample {highcharts} highcharts/chart/events-render/
  2495. * Load vs Redraw vs Render
  2496. *
  2497. * @type {Highcharts.ChartRenderCallbackFunction}
  2498. * @since 5.0.7
  2499. * @context Highcharts.Chart
  2500. * @apioption chart.events.render
  2501. */
  2502. /**
  2503. * Fires when an area of the chart has been selected. Selection is
  2504. * enabled by setting the chart's zoomType. One parameter, `event`, is
  2505. * passed to the function, containing common event information. The
  2506. * default action for the selection event is to zoom the chart to the
  2507. * selected area. It can be prevented by calling
  2508. * `event.preventDefault()` or return false.
  2509. *
  2510. * Information on the selected area can be found through `event.xAxis`
  2511. * and `event.yAxis`, which are arrays containing the axes of each
  2512. * dimension and each axis' min and max values. The primary axes are
  2513. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  2514. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  2515. *
  2516. * ```js
  2517. * selection: function(event) {
  2518. * // log the min and max of the primary, datetime x-axis
  2519. * console.log(
  2520. * Highcharts.dateFormat(
  2521. * '%Y-%m-%d %H:%M:%S',
  2522. * event.xAxis[0].min
  2523. * ),
  2524. * Highcharts.dateFormat(
  2525. * '%Y-%m-%d %H:%M:%S',
  2526. * event.xAxis[0].max
  2527. * )
  2528. * );
  2529. * // log the min and max of the y axis
  2530. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  2531. * }
  2532. * ```
  2533. *
  2534. * @sample {highcharts} highcharts/chart/events-selection/
  2535. * Report on selection and reset
  2536. * @sample {highcharts} highcharts/chart/events-selection-points/
  2537. * Select a range of points through a drag selection
  2538. * @sample {highstock} stock/chart/events-selection/
  2539. * Report on selection and reset
  2540. * @sample {highstock} highcharts/chart/events-selection-points/
  2541. * Select a range of points through a drag selection
  2542. * (Highcharts)
  2543. *
  2544. * @type {Highcharts.ChartSelectionCallbackFunction}
  2545. * @apioption chart.events.selection
  2546. */
  2547. /**
  2548. * The margin between the outer edge of the chart and the plot area.
  2549. * The numbers in the array designate top, right, bottom and left
  2550. * respectively. Use the options `marginTop`, `marginRight`,
  2551. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  2552. *
  2553. * By default there is no margin. The actual space is dynamically
  2554. * calculated from the offset of axis labels, axis title, title,
  2555. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  2556. * `spacingBottom` and `spacingLeft` options.
  2557. *
  2558. * @sample {highcharts} highcharts/chart/margins-zero/
  2559. * Zero margins
  2560. * @sample {highstock} stock/chart/margin-zero/
  2561. * Zero margins
  2562. *
  2563. * @type {number|Array<number>}
  2564. * @apioption chart.margin
  2565. */
  2566. /**
  2567. * The margin between the bottom outer edge of the chart and the plot
  2568. * area. Use this to set a fixed pixel value for the margin as opposed
  2569. * to the default dynamic margin. See also `spacingBottom`.
  2570. *
  2571. * @sample {highcharts} highcharts/chart/marginbottom/
  2572. * 100px bottom margin
  2573. * @sample {highstock} stock/chart/marginbottom/
  2574. * 100px bottom margin
  2575. * @sample {highmaps} maps/chart/margin/
  2576. * 100px margins
  2577. *
  2578. * @type {number}
  2579. * @since 2.0
  2580. * @apioption chart.marginBottom
  2581. */
  2582. /**
  2583. * The margin between the left outer edge of the chart and the plot
  2584. * area. Use this to set a fixed pixel value for the margin as opposed
  2585. * to the default dynamic margin. See also `spacingLeft`.
  2586. *
  2587. * @sample {highcharts} highcharts/chart/marginleft/
  2588. * 150px left margin
  2589. * @sample {highstock} stock/chart/marginleft/
  2590. * 150px left margin
  2591. * @sample {highmaps} maps/chart/margin/
  2592. * 100px margins
  2593. *
  2594. * @type {number}
  2595. * @since 2.0
  2596. * @apioption chart.marginLeft
  2597. */
  2598. /**
  2599. * The margin between the right outer edge of the chart and the plot
  2600. * area. Use this to set a fixed pixel value for the margin as opposed
  2601. * to the default dynamic margin. See also `spacingRight`.
  2602. *
  2603. * @sample {highcharts} highcharts/chart/marginright/
  2604. * 100px right margin
  2605. * @sample {highstock} stock/chart/marginright/
  2606. * 100px right margin
  2607. * @sample {highmaps} maps/chart/margin/
  2608. * 100px margins
  2609. *
  2610. * @type {number}
  2611. * @since 2.0
  2612. * @apioption chart.marginRight
  2613. */
  2614. /**
  2615. * The margin between the top outer edge of the chart and the plot area.
  2616. * Use this to set a fixed pixel value for the margin as opposed to
  2617. * the default dynamic margin. See also `spacingTop`.
  2618. *
  2619. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  2620. * @sample {highstock} stock/chart/margintop/
  2621. * 100px top margin
  2622. * @sample {highmaps} maps/chart/margin/
  2623. * 100px margins
  2624. *
  2625. * @type {number}
  2626. * @since 2.0
  2627. * @apioption chart.marginTop
  2628. */
  2629. /**
  2630. * Callback function to override the default function that formats all
  2631. * the numbers in the chart. Returns a string with the formatted number.
  2632. *
  2633. * @sample highcharts/members/highcharts-numberformat
  2634. * Arabic digits in Highcharts
  2635. * @type {Highcharts.NumberFormatterCallbackFunction}
  2636. * @since 8.0.0
  2637. * @apioption chart.numberFormatter
  2638. */
  2639. /**
  2640. * Allows setting a key to switch between zooming and panning. Can be
  2641. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  2642. * key on Windows) or `shift`. The keys are mapped directly to the key
  2643. * properties of the click event argument (`event.altKey`,
  2644. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  2645. *
  2646. * @type {string}
  2647. * @since 4.0.3
  2648. * @product highcharts gantt
  2649. * @validvalue ["alt", "ctrl", "meta", "shift"]
  2650. * @apioption chart.panKey
  2651. */
  2652. /**
  2653. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  2654. * to combine zooming and panning.
  2655. *
  2656. * On touch devices, when the [tooltip.followTouchMove](
  2657. * #tooltip.followTouchMove) option is `true` (default), panning
  2658. * requires two fingers. To allow panning with one finger, set
  2659. * `followTouchMove` to `false`.
  2660. *
  2661. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  2662. * @sample {highstock} stock/chart/panning/ Zooming and xy panning
  2663. */
  2664. panning: {
  2665. /**
  2666. * Enable or disable chart panning.
  2667. *
  2668. * @type {boolean}
  2669. * @default {highcharts} false
  2670. * @default {highstock|highmaps} true
  2671. */
  2672. enabled: false,
  2673. /**
  2674. * Decides in what dimensions the user can pan the chart. Can be
  2675. * one of `x`, `y`, or `xy`.
  2676. *
  2677. * When this option is set to `y` or `xy`, [yAxis.startOnTick](#yAxis.startOnTick)
  2678. * and [yAxis.endOnTick](#yAxis.endOnTick) are overwritten to `false`.
  2679. *
  2680. * @sample {highcharts} highcharts/chart/panning-type
  2681. * Zooming and xy panning
  2682. *
  2683. * @declare Highcharts.OptionsChartPanningTypeValue
  2684. * @type {string}
  2685. * @validvalue ["x", "y", "xy"]
  2686. * @default {highcharts|highstock} x
  2687. * @product highcharts highstock gantt
  2688. */
  2689. type: 'x'
  2690. },
  2691. /**
  2692. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  2693. * gestures only. By default, the `pinchType` is the same as the
  2694. * `zoomType` setting. However, pinching can be enabled separately in
  2695. * some cases, for example in stock charts where a mouse drag pans the
  2696. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  2697. * #tooltip.followTouchMove) is true, pinchType only applies to
  2698. * two-finger touches.
  2699. *
  2700. * @type {string}
  2701. * @default {highcharts} undefined
  2702. * @default {highstock} undefined
  2703. * @since 3.0
  2704. * @product highcharts highstock gantt
  2705. * @deprecated
  2706. * @validvalue ["x", "y", "xy"]
  2707. * @apioption chart.pinchType
  2708. */
  2709. /**
  2710. * Whether to apply styled mode. When in styled mode, no presentational
  2711. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  2712. * are required to style the chart. The default style sheet is
  2713. * available from `https://code.highcharts.com/css/highcharts.css`.
  2714. *
  2715. * [Read more in the docs](https://www.highcharts.com/docs/chart-design-and-style/style-by-css)
  2716. * on what classes and variables are available.
  2717. *
  2718. * @sample highcharts/css/colors
  2719. * Color theming with CSS
  2720. * @sample highcharts/css/prefers-color-scheme
  2721. * Dynamic theme based on system settings
  2722. * @type {boolean}
  2723. * @default false
  2724. * @since 7.0
  2725. * @apioption chart.styledMode
  2726. */
  2727. styledMode: false,
  2728. /**
  2729. * The corner radius of the outer chart border.
  2730. *
  2731. * @sample {highcharts} highcharts/chart/borderradius/
  2732. * 20px radius
  2733. * @sample {highstock} stock/chart/border/
  2734. * 10px radius
  2735. * @sample {highmaps} maps/chart/border/
  2736. * Border options
  2737. *
  2738. */
  2739. borderRadius: 0,
  2740. /**
  2741. * In styled mode, this sets how many colors the class names
  2742. * should rotate between. With ten colors, series (or points) are
  2743. * given class names like `highcharts-color-0`, `highcharts-color-1`
  2744. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  2745. * is to set colors using the [colors](#colors) setting.
  2746. *
  2747. * @since 5.0.0
  2748. */
  2749. colorCount: 10,
  2750. /**
  2751. * By default, (because of memory and performance reasons) the chart does
  2752. * not copy the data but keeps it as a reference. In some cases, this might
  2753. * result in mutating the original data source. In order to prevent that,
  2754. * set that property to false. Please note that changing that might decrease
  2755. * performance, especially with bigger sets of data.
  2756. *
  2757. * @type {boolean}
  2758. * @since 10.1.0
  2759. */
  2760. allowMutatingData: true,
  2761. /**
  2762. * If true, the axes will scale to the remaining visible series once
  2763. * one series is hidden. If false, hiding and showing a series will
  2764. * not affect the axes or the other series. For stacks, once one series
  2765. * within the stack is hidden, the rest of the stack will close in
  2766. * around it even if the axis is not affected.
  2767. *
  2768. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  2769. * True by default
  2770. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  2771. * False
  2772. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  2773. * True with stack
  2774. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  2775. * True by default
  2776. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  2777. * False
  2778. *
  2779. * @since 1.2.0
  2780. * @product highcharts highstock gantt
  2781. */
  2782. ignoreHiddenSeries: true,
  2783. /**
  2784. * Whether to invert the axes so that the x axis is vertical and y axis
  2785. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  2786. * by default.
  2787. *
  2788. * @productdesc {highcharts}
  2789. * If a bar series is present in the chart, it will be inverted
  2790. * automatically. Inverting the chart doesn't have an effect if there
  2791. * are no cartesian series in the chart, or if the chart is
  2792. * [polar](#chart.polar).
  2793. *
  2794. * @sample {highcharts} highcharts/chart/inverted/
  2795. * Inverted line
  2796. * @sample {highstock} stock/navigator/inverted/
  2797. * Inverted stock chart
  2798. *
  2799. * @type {boolean}
  2800. * @default false
  2801. * @product highcharts highstock gantt
  2802. * @apioption chart.inverted
  2803. */
  2804. /**
  2805. * The distance between the outer edge of the chart and the content,
  2806. * like title or legend, or axis title and labels if present. The
  2807. * numbers in the array designate top, right, bottom and left
  2808. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  2809. * and spacingLeft options for shorthand setting of one option.
  2810. *
  2811. * @type {Array<number>}
  2812. * @see [chart.margin](#chart.margin)
  2813. * @default [10, 10, 15, 10]
  2814. * @since 3.0.6
  2815. */
  2816. spacing: [10, 10, 15, 10],
  2817. /**
  2818. * The button that appears after a selection zoom, allowing the user
  2819. * to reset zoom.
  2820. *
  2821. * @since 2.2
  2822. * @deprecated 10.2.1
  2823. */
  2824. resetZoomButton: {
  2825. /**
  2826. * What frame the button placement should be related to. Can be
  2827. * either `plotBox` or `spacingBox`.
  2828. *
  2829. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  2830. * Relative to the chart
  2831. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  2832. * Relative to the chart
  2833. *
  2834. * @type {Highcharts.ButtonRelativeToValue}
  2835. * @default plot
  2836. * @apioption chart.resetZoomButton.relativeTo
  2837. */
  2838. /**
  2839. * A collection of attributes for the button. The object takes SVG
  2840. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  2841. * border radius. The theme also supports `style`, a collection of
  2842. * CSS properties for the text. Equivalent attributes for the hover
  2843. * state are given in `theme.states.hover`.
  2844. *
  2845. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  2846. * Theming the button
  2847. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  2848. * Theming the button
  2849. *
  2850. * @type {Highcharts.SVGAttributes}
  2851. */
  2852. theme: {
  2853. /**
  2854. * @internal
  2855. */
  2856. zIndex: 6
  2857. },
  2858. /**
  2859. * The position of the button.
  2860. *
  2861. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  2862. * Above the plot area
  2863. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  2864. * Above the plot area
  2865. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  2866. * Above the plot area
  2867. *
  2868. * @type {Highcharts.AlignObject}
  2869. */
  2870. position: {
  2871. /**
  2872. * The horizontal alignment of the button.
  2873. */
  2874. align: 'right',
  2875. /**
  2876. * The horizontal offset of the button.
  2877. */
  2878. x: -10,
  2879. /**
  2880. * The vertical alignment of the button.
  2881. *
  2882. * @type {Highcharts.VerticalAlignValue}
  2883. * @default top
  2884. * @apioption chart.resetZoomButton.position.verticalAlign
  2885. */
  2886. /**
  2887. * The vertical offset of the button.
  2888. */
  2889. y: 10
  2890. }
  2891. },
  2892. /**
  2893. * The pixel width of the plot area border.
  2894. *
  2895. * @sample {highcharts} highcharts/chart/plotborderwidth/
  2896. * 1px border
  2897. * @sample {highstock} stock/chart/plotborder/
  2898. * 2px border
  2899. * @sample {highmaps} maps/chart/plotborder/
  2900. * Plot border options
  2901. *
  2902. * @type {number}
  2903. * @default 0
  2904. * @apioption chart.plotBorderWidth
  2905. */
  2906. /**
  2907. * Whether to apply a drop shadow to the plot area. Requires that
  2908. * plotBackgroundColor be set. The shadow can be an object configuration
  2909. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  2910. *
  2911. * @sample {highcharts} highcharts/chart/plotshadow/
  2912. * Plot shadow
  2913. * @sample {highstock} stock/chart/plotshadow/
  2914. * Plot shadow
  2915. * @sample {highmaps} maps/chart/plotborder/
  2916. * Plot border options
  2917. *
  2918. * @type {boolean|Highcharts.ShadowOptionsObject}
  2919. * @default false
  2920. * @apioption chart.plotShadow
  2921. */
  2922. /**
  2923. * When true, cartesian charts like line, spline, area and column are
  2924. * transformed into the polar coordinate system. This produces _polar
  2925. * charts_, also known as _radar charts_.
  2926. *
  2927. * @sample {highcharts} highcharts/demo/polar/
  2928. * Polar chart
  2929. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  2930. * Wind rose, stacked polar column chart
  2931. * @sample {highcharts} highcharts/demo/polar-spider/
  2932. * Spider web chart
  2933. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  2934. * Star plot, multivariate data in a polar chart
  2935. *
  2936. * @type {boolean}
  2937. * @default false
  2938. * @since 2.3.0
  2939. * @product highcharts
  2940. * @requires highcharts-more
  2941. * @apioption chart.polar
  2942. */
  2943. /**
  2944. * Whether to reflow the chart to fit the width of the container div
  2945. * on resizing the window.
  2946. *
  2947. * @sample {highcharts} highcharts/chart/reflow-true/
  2948. * True by default
  2949. * @sample {highcharts} highcharts/chart/reflow-false/
  2950. * False
  2951. * @sample {highstock} stock/chart/reflow-true/
  2952. * True by default
  2953. * @sample {highstock} stock/chart/reflow-false/
  2954. * False
  2955. * @sample {highmaps} maps/chart/reflow-true/
  2956. * True by default
  2957. * @sample {highmaps} maps/chart/reflow-false/
  2958. * False
  2959. *
  2960. * @since 2.1
  2961. */
  2962. reflow: true,
  2963. /**
  2964. * The HTML element where the chart will be rendered. If it is a string,
  2965. * the element by that id is used. The HTML element can also be passed
  2966. * by direct reference, or as the first argument of the chart
  2967. * constructor, in which case the option is not needed.
  2968. *
  2969. * @sample {highcharts} highcharts/chart/reflow-true/
  2970. * String
  2971. * @sample {highcharts} highcharts/chart/renderto-object/
  2972. * Object reference
  2973. * @sample {highstock} stock/chart/renderto-string/
  2974. * String
  2975. * @sample {highstock} stock/chart/renderto-object/
  2976. * Object reference
  2977. *
  2978. * @type {string|Highcharts.HTMLDOMElement}
  2979. * @apioption chart.renderTo
  2980. */
  2981. /**
  2982. * The background color of the marker square when selecting (zooming
  2983. * in on) an area of the chart.
  2984. *
  2985. * @see In styled mode, the selection marker fill is set with the
  2986. * `.highcharts-selection-marker` class.
  2987. *
  2988. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  2989. * @default rgba(51,92,173,0.25)
  2990. * @since 2.1.7
  2991. * @apioption chart.selectionMarkerFill
  2992. */
  2993. /**
  2994. * Whether to apply a drop shadow to the global series group. This causes
  2995. * all the series to have the same shadow. Contrary to the `series.shadow`
  2996. * option, this prevents items from casting shadows on each other, like for
  2997. * others series in a stack. The shadow can be an object configuration
  2998. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  2999. *
  3000. * @sample highcharts/chart/seriesgroupshadow/ Shadow
  3001. *
  3002. * @type {boolean|Highcharts.ShadowOptionsObject}
  3003. * @default false
  3004. * @apioption chart.shadow
  3005. */
  3006. /**
  3007. * Whether to apply a drop shadow to the outer chart area. Requires
  3008. * that backgroundColor be set. The shadow can be an object
  3009. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  3010. * `width`.
  3011. *
  3012. * @sample {highcharts} highcharts/chart/shadow/
  3013. * Shadow
  3014. * @sample {highstock} stock/chart/shadow/
  3015. * Shadow
  3016. * @sample {highmaps} maps/chart/border/
  3017. * Chart border and shadow
  3018. *
  3019. * @type {boolean|Highcharts.ShadowOptionsObject}
  3020. * @default false
  3021. * @apioption chart.shadow
  3022. */
  3023. /**
  3024. * Whether to show the axes initially. This only applies to empty charts
  3025. * where series are added dynamically, as axes are automatically added
  3026. * to cartesian series.
  3027. *
  3028. * @sample {highcharts} highcharts/chart/showaxes-false/
  3029. * False by default
  3030. * @sample {highcharts} highcharts/chart/showaxes-true/
  3031. * True
  3032. *
  3033. * @type {boolean}
  3034. * @since 1.2.5
  3035. * @product highcharts gantt
  3036. * @apioption chart.showAxes
  3037. */
  3038. /**
  3039. * The space between the bottom edge of the chart and the content (plot
  3040. * area, axis title and labels, title, subtitle or legend in top
  3041. * position).
  3042. *
  3043. * @sample {highcharts} highcharts/chart/spacingbottom/
  3044. * Spacing bottom set to 100
  3045. * @sample {highstock} stock/chart/spacingbottom/
  3046. * Spacing bottom set to 100
  3047. * @sample {highmaps} maps/chart/spacing/
  3048. * Spacing 100 all around
  3049. *
  3050. * @type {number}
  3051. * @default 15
  3052. * @since 2.1
  3053. * @apioption chart.spacingBottom
  3054. */
  3055. /**
  3056. * The space between the left edge of the chart and the content (plot
  3057. * area, axis title and labels, title, subtitle or legend in top
  3058. * position).
  3059. *
  3060. * @sample {highcharts} highcharts/chart/spacingleft/
  3061. * Spacing left set to 100
  3062. * @sample {highstock} stock/chart/spacingleft/
  3063. * Spacing left set to 100
  3064. * @sample {highmaps} maps/chart/spacing/
  3065. * Spacing 100 all around
  3066. *
  3067. * @type {number}
  3068. * @default 10
  3069. * @since 2.1
  3070. * @apioption chart.spacingLeft
  3071. */
  3072. /**
  3073. * The space between the right edge of the chart and the content (plot
  3074. * area, axis title and labels, title, subtitle or legend in top
  3075. * position).
  3076. *
  3077. * @sample {highcharts} highcharts/chart/spacingright-100/
  3078. * Spacing set to 100
  3079. * @sample {highcharts} highcharts/chart/spacingright-legend/
  3080. * Legend in right position with default spacing
  3081. * @sample {highstock} stock/chart/spacingright/
  3082. * Spacing set to 100
  3083. * @sample {highmaps} maps/chart/spacing/
  3084. * Spacing 100 all around
  3085. *
  3086. * @type {number}
  3087. * @default 10
  3088. * @since 2.1
  3089. * @apioption chart.spacingRight
  3090. */
  3091. /**
  3092. * The space between the top edge of the chart and the content (plot
  3093. * area, axis title and labels, title, subtitle or legend in top
  3094. * position).
  3095. *
  3096. * @sample {highcharts} highcharts/chart/spacingtop-100/
  3097. * A top spacing of 100
  3098. * @sample {highcharts} highcharts/chart/spacingtop-10/
  3099. * Floating chart title makes the plot area align to the default
  3100. * spacingTop of 10.
  3101. * @sample {highstock} stock/chart/spacingtop/
  3102. * A top spacing of 100
  3103. * @sample {highmaps} maps/chart/spacing/
  3104. * Spacing 100 all around
  3105. *
  3106. * @type {number}
  3107. * @default 10
  3108. * @since 2.1
  3109. * @apioption chart.spacingTop
  3110. */
  3111. /**
  3112. * Additional CSS styles to apply inline to the container `div` and the root
  3113. * SVG.
  3114. *
  3115. * Since v11, the root font size is 1rem by default, and all child element
  3116. * are given a relative `em` font size by default. This allows implementers
  3117. * to control all the chart's font sizes by only setting the root level.
  3118. *
  3119. * @see In styled mode, general chart styles can be set with the
  3120. * `.highcharts-root` class.
  3121. * @sample {highcharts} highcharts/chart/style-serif-font/
  3122. * Using a serif type font
  3123. * @sample {highcharts} highcharts/members/relative-font-size/
  3124. * Relative font sizes
  3125. * @sample {highcharts} highcharts/css/em/
  3126. * Styled mode with relative font sizes
  3127. * @sample {highstock} stock/chart/style/
  3128. * Using a serif type font
  3129. * @sample {highmaps} maps/chart/style-serif-font/
  3130. * Using a serif type font
  3131. *
  3132. * @type {Highcharts.CSSObject}
  3133. * @default {"fontFamily": Helvetica, Arial, sans-serif","fontSize":"1rem"}
  3134. * @apioption chart.style
  3135. */
  3136. /**
  3137. * The default series type for the chart. Can be any of the chart types
  3138. * listed under [plotOptions](#plotOptions) and [series](#series) or can
  3139. * be a series provided by an additional module.
  3140. *
  3141. * In TypeScript this option has no effect in sense of typing and
  3142. * instead the `type` option must always be set in the series.
  3143. *
  3144. * @sample {highcharts} highcharts/chart/type-bar/
  3145. * Bar
  3146. * @sample {highstock} stock/chart/type/
  3147. * Areaspline
  3148. * @sample {highmaps} maps/chart/type-mapline/
  3149. * Mapline
  3150. *
  3151. * @type {string}
  3152. * @default {highcharts} line
  3153. * @default {highstock} line
  3154. * @default {highmaps} map
  3155. * @since 2.1.0
  3156. * @apioption chart.type
  3157. */
  3158. type: 'line',
  3159. /**
  3160. * Decides in what dimensions the user can zoom by dragging the mouse.
  3161. * Can be one of `x`, `y` or `xy`.
  3162. *
  3163. * @see [panKey](#chart.panKey)
  3164. *
  3165. * @sample {highcharts} highcharts/chart/zoomtype-none/
  3166. * None by default
  3167. * @sample {highcharts} highcharts/chart/zoomtype-x/
  3168. * X
  3169. * @sample {highcharts} highcharts/chart/zoomtype-y/
  3170. * Y
  3171. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  3172. * Xy
  3173. * @sample {highcharts} highcharts/chart/zoomtype-polar/
  3174. * Zoom on polar chart
  3175. * @sample {highstock} stock/demo/basic-line/
  3176. * None by default
  3177. * @sample {highstock} stock/chart/zoomtype-x/
  3178. * X
  3179. * @sample {highstock} stock/chart/zoomtype-y/
  3180. * Y
  3181. * @sample {highstock} stock/chart/zoomtype-xy/
  3182. * Xy
  3183. * @sample {highmaps} maps/chart/zoomtype-xy/
  3184. * Map with selection zoom
  3185. *
  3186. * @type {string}
  3187. * @validvalue ["x", "y", "xy"]
  3188. * @deprecated
  3189. * @apioption chart.zoomType
  3190. */
  3191. /**
  3192. * Enables zooming by a single touch, in combination with
  3193. * [chart.zoomType](#chart.zoomType). When enabled, two-finger pinch
  3194. * will still work as set up by [chart.pinchType](#chart.pinchType).
  3195. * However, `zoomBySingleTouch` will interfere with touch-dragging the
  3196. * chart to read the tooltip. And especially when vertical zooming is
  3197. * enabled, it will make it hard to scroll vertically on the page.
  3198. * @since 9.0.0
  3199. * @sample highcharts/chart/zoombysingletouch
  3200. * Zoom by single touch enabled, with buttons to toggle
  3201. * @product highcharts highstock gantt
  3202. * @deprecated
  3203. */
  3204. /**
  3205. * Chart zooming options.
  3206. * @since 10.2.1
  3207. */
  3208. zooming: {
  3209. /**
  3210. * Equivalent to [type](#chart.zooming.type), but for multitouch
  3211. * gestures only. By default, the `pinchType` is the same as the
  3212. * `type` setting. However, pinching can be enabled separately in
  3213. * some cases, for example in stock charts where a mouse drag pans the
  3214. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  3215. * #tooltip.followTouchMove) is true, pinchType only applies to
  3216. * two-finger touches.
  3217. *
  3218. * @type {string}
  3219. * @default {highcharts} undefined
  3220. * @default {highstock} x
  3221. * @product highcharts highstock gantt
  3222. * @validvalue ["x", "y", "xy"]
  3223. * @apioption chart.zooming.pinchType
  3224. */
  3225. /**
  3226. * Decides in what dimensions the user can zoom by dragging the mouse.
  3227. * Can be one of `x`, `y` or `xy`.
  3228. *
  3229. * @declare Highcharts.OptionsChartZoomingTypeValue
  3230. * @type {string}
  3231. * @default {highcharts} undefined
  3232. * @product highcharts highstock gantt
  3233. * @validvalue ["x", "y", "xy"]
  3234. * @apioption chart.zooming.type
  3235. */
  3236. /**
  3237. * Set a key to hold when dragging to zoom the chart. This is useful to
  3238. * avoid zooming while moving points. Should be set different than
  3239. * [chart.panKey](#chart.panKey).
  3240. *
  3241. * @type {string}
  3242. * @default {highcharts} undefined
  3243. * @validvalue ["alt", "ctrl", "meta", "shift"]
  3244. * @requires modules/draggable-points
  3245. * @apioption chart.zooming.key
  3246. */
  3247. /**
  3248. * Enables zooming by a single touch, in combination with
  3249. * [chart.zooming.type](#chart.zooming.type). When enabled, two-finger
  3250. * pinch will still work as set up by [chart.zooming.pinchType]
  3251. * (#chart.zooming.pinchType). However, `singleTouch` will interfere
  3252. * with touch-dragging the chart to read the tooltip. And especially
  3253. * when vertical zooming is enabled, it will make it hard to scroll
  3254. * vertically on the page.
  3255. *
  3256. * @sample highcharts/chart/zoombysingletouch
  3257. * Zoom by single touch enabled, with buttons to toggle
  3258. *
  3259. * @product highcharts highstock gantt
  3260. */
  3261. singleTouch: false,
  3262. /**
  3263. * The button that appears after a selection zoom, allowing the user
  3264. * to reset zoom.
  3265. */
  3266. resetButton: {
  3267. /**
  3268. * What frame the button placement should be related to. Can be
  3269. * either `plotBox` or `spacingBox`.
  3270. *
  3271. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  3272. * Relative to the chart
  3273. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  3274. * Relative to the chart
  3275. *
  3276. * @type {Highcharts.ButtonRelativeToValue}
  3277. * @default plot
  3278. * @apioption chart.zooming.resetButton.relativeTo
  3279. */
  3280. /**
  3281. * A collection of attributes for the button. The object takes SVG
  3282. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  3283. * border radius. The theme also supports `style`, a collection of
  3284. * CSS properties for the text. Equivalent attributes for the hover
  3285. * state are given in `theme.states.hover`.
  3286. *
  3287. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  3288. * Theming the button
  3289. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  3290. * Theming the button
  3291. *
  3292. * @type {Highcharts.SVGAttributes}
  3293. * @since 10.2.1
  3294. */
  3295. theme: {
  3296. /** @internal */
  3297. zIndex: 6
  3298. },
  3299. /**
  3300. * The position of the button.
  3301. *
  3302. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  3303. * Above the plot area
  3304. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  3305. * Above the plot area
  3306. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  3307. * Above the plot area
  3308. *
  3309. * @type {Highcharts.AlignObject}
  3310. * @since 10.2.1
  3311. */
  3312. position: {
  3313. /**
  3314. * The horizontal alignment of the button.
  3315. */
  3316. align: 'right',
  3317. /**
  3318. * The horizontal offset of the button.
  3319. */
  3320. x: -10,
  3321. /**
  3322. * The vertical alignment of the button.
  3323. *
  3324. * @type {Highcharts.VerticalAlignValue}
  3325. * @default top
  3326. * @apioption chart.zooming.resetButton.position.verticalAlign
  3327. */
  3328. /**
  3329. * The vertical offset of the button.
  3330. */
  3331. y: 10
  3332. }
  3333. }
  3334. },
  3335. /**
  3336. * An explicit width for the chart. By default (when `null`) the width
  3337. * is calculated from the offset width of the containing element.
  3338. *
  3339. * @sample {highcharts} highcharts/chart/width/
  3340. * 800px wide
  3341. * @sample {highstock} stock/chart/width/
  3342. * 800px wide
  3343. * @sample {highmaps} maps/chart/size/
  3344. * Chart with explicit size
  3345. *
  3346. * @type {null|number|string}
  3347. */
  3348. width: null,
  3349. /**
  3350. * An explicit height for the chart. If a _number_, the height is
  3351. * given in pixels. If given a _percentage string_ (for example
  3352. * `'56%'`), the height is given as the percentage of the actual chart
  3353. * width. This allows for preserving the aspect ratio across responsive
  3354. * sizes.
  3355. *
  3356. * By default (when `null`) the height is calculated from the offset
  3357. * height of the containing element, or 400 pixels if the containing
  3358. * element's height is 0.
  3359. *
  3360. * @sample {highcharts} highcharts/chart/height/
  3361. * 500px height
  3362. * @sample {highstock} stock/chart/height/
  3363. * 300px height
  3364. * @sample {highmaps} maps/chart/size/
  3365. * Chart with explicit size
  3366. * @sample highcharts/chart/height-percent/
  3367. * Highcharts with percentage height
  3368. *
  3369. * @type {null|number|string}
  3370. */
  3371. height: null,
  3372. /**
  3373. * The color of the outer chart border.
  3374. *
  3375. * @see In styled mode, the stroke is set with the
  3376. * `.highcharts-background` class.
  3377. *
  3378. * @sample {highcharts} highcharts/chart/bordercolor/
  3379. * Brown border
  3380. * @sample {highstock} stock/chart/border/
  3381. * Brown border
  3382. * @sample {highmaps} maps/chart/border/
  3383. * Border options
  3384. *
  3385. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3386. */
  3387. borderColor: "#334eff" /* Palette.highlightColor80 */,
  3388. /**
  3389. * The pixel width of the outer chart border.
  3390. *
  3391. * @see In styled mode, the stroke is set with the
  3392. * `.highcharts-background` class.
  3393. *
  3394. * @sample {highcharts} highcharts/chart/borderwidth/
  3395. * 5px border
  3396. * @sample {highstock} stock/chart/border/
  3397. * 2px border
  3398. * @sample {highmaps} maps/chart/border/
  3399. * Border options
  3400. *
  3401. * @type {number}
  3402. * @default 0
  3403. * @apioption chart.borderWidth
  3404. */
  3405. /**
  3406. * The background color or gradient for the outer chart area.
  3407. *
  3408. * @see In styled mode, the background is set with the
  3409. * `.highcharts-background` class.
  3410. *
  3411. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  3412. * Color
  3413. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  3414. * Gradient
  3415. * @sample {highstock} stock/chart/backgroundcolor-color/
  3416. * Color
  3417. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  3418. * Gradient
  3419. * @sample {highmaps} maps/chart/backgroundcolor-color/
  3420. * Color
  3421. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  3422. * Gradient
  3423. *
  3424. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3425. */
  3426. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  3427. /**
  3428. * The background color or gradient for the plot area.
  3429. *
  3430. * @see In styled mode, the plot background is set with the
  3431. * `.highcharts-plot-background` class.
  3432. *
  3433. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  3434. * Color
  3435. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  3436. * Gradient
  3437. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  3438. * Color
  3439. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  3440. * Gradient
  3441. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  3442. * Color
  3443. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  3444. * Gradient
  3445. *
  3446. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3447. * @apioption chart.plotBackgroundColor
  3448. */
  3449. /**
  3450. * The URL for an image to use as the plot background. To set an image
  3451. * as the background for the entire chart, set a CSS background image
  3452. * to the container element. Note that for the image to be applied to
  3453. * exported charts, its URL needs to be accessible by the export server.
  3454. *
  3455. * @see In styled mode, a plot background image can be set with the
  3456. * `.highcharts-plot-background` class and a [custom pattern](
  3457. * https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  3458. *
  3459. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  3460. * Skies
  3461. * @sample {highstock} stock/chart/plotbackgroundimage/
  3462. * Skies
  3463. *
  3464. * @type {string}
  3465. * @apioption chart.plotBackgroundImage
  3466. */
  3467. /**
  3468. * The color of the inner chart or plot area border.
  3469. *
  3470. * @see In styled mode, a plot border stroke can be set with the
  3471. * `.highcharts-plot-border` class.
  3472. *
  3473. * @sample {highcharts} highcharts/chart/plotbordercolor/
  3474. * Blue border
  3475. * @sample {highstock} stock/chart/plotborder/
  3476. * Blue border
  3477. * @sample {highmaps} maps/chart/plotborder/
  3478. * Plot border options
  3479. *
  3480. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3481. */
  3482. plotBorderColor: "#cccccc" /* Palette.neutralColor20 */
  3483. };
  3484. /* *
  3485. *
  3486. * Default Export
  3487. *
  3488. * */
  3489. return ChartDefaults;
  3490. });
  3491. _registerModule(_modules, 'Core/Color/Color.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  3492. /* *
  3493. *
  3494. * (c) 2010-2021 Torstein Honsi
  3495. *
  3496. * License: www.highcharts.com/license
  3497. *
  3498. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3499. *
  3500. * */
  3501. const { isNumber, merge, pInt } = U;
  3502. /* *
  3503. *
  3504. * Class
  3505. *
  3506. * */
  3507. /* eslint-disable valid-jsdoc */
  3508. /**
  3509. * Handle color operations. Some object methods are chainable.
  3510. *
  3511. * @class
  3512. * @name Highcharts.Color
  3513. *
  3514. * @param {Highcharts.ColorType} input
  3515. * The input color in either rbga or hex format
  3516. */
  3517. class Color {
  3518. /* *
  3519. *
  3520. * Static Functions
  3521. *
  3522. * */
  3523. /**
  3524. * Creates a color instance out of a color string or object.
  3525. *
  3526. * @function Highcharts.Color.parse
  3527. *
  3528. * @param {Highcharts.ColorType} [input]
  3529. * The input color in either rbga or hex format.
  3530. *
  3531. * @return {Highcharts.Color}
  3532. * Color instance.
  3533. */
  3534. static parse(input) {
  3535. return input ? new Color(input) : Color.None;
  3536. }
  3537. /* *
  3538. *
  3539. * Constructor
  3540. *
  3541. * */
  3542. constructor(input) {
  3543. this.rgba = [NaN, NaN, NaN, NaN];
  3544. this.input = input;
  3545. const GlobalColor = H.Color;
  3546. // Backwards compatibility, allow class overwrite
  3547. if (GlobalColor && GlobalColor !== Color) {
  3548. return new GlobalColor(input);
  3549. }
  3550. this.init(input);
  3551. }
  3552. /* *
  3553. *
  3554. * Functions
  3555. *
  3556. * */
  3557. /**
  3558. * Parse the input color to rgba array
  3559. *
  3560. * @private
  3561. * @function Highcharts.Color#init
  3562. *
  3563. * @param {Highcharts.ColorType} input
  3564. * The input color in either rbga or hex format
  3565. */
  3566. init(input) {
  3567. let result, rgba, i, parser;
  3568. // Gradients
  3569. if (typeof input === 'object' &&
  3570. typeof input.stops !== 'undefined') {
  3571. this.stops = input.stops.map((stop) => new Color(stop[1]));
  3572. // Solid colors
  3573. }
  3574. else if (typeof input === 'string') {
  3575. this.input = input = (Color.names[input.toLowerCase()] || input);
  3576. // Bitmasking as input[0] is not working for legacy IE.
  3577. if (input.charAt(0) === '#') {
  3578. const len = input.length, col = parseInt(input.substr(1), 16);
  3579. // Handle long-form, e.g. #AABBCC
  3580. if (len === 7) {
  3581. rgba = [
  3582. (col & 0xFF0000) >> 16,
  3583. (col & 0xFF00) >> 8,
  3584. (col & 0xFF),
  3585. 1
  3586. ];
  3587. // Handle short-form, e.g. #ABC
  3588. // In short form, the value is assumed to be the same
  3589. // for both nibbles for each component. e.g. #ABC = #AABBCC
  3590. }
  3591. else if (len === 4) {
  3592. rgba = [
  3593. (((col & 0xF00) >> 4) |
  3594. (col & 0xF00) >> 8),
  3595. (((col & 0xF0) >> 4) |
  3596. (col & 0xF0)),
  3597. ((col & 0xF) << 4) | (col & 0xF),
  3598. 1
  3599. ];
  3600. }
  3601. }
  3602. // Otherwise, check regex parsers
  3603. if (!rgba) {
  3604. i = Color.parsers.length;
  3605. while (i-- && !rgba) {
  3606. parser = Color.parsers[i];
  3607. result = parser.regex.exec(input);
  3608. if (result) {
  3609. rgba = parser.parse(result);
  3610. }
  3611. }
  3612. }
  3613. }
  3614. if (rgba) {
  3615. this.rgba = rgba;
  3616. }
  3617. }
  3618. /**
  3619. * Return the color or gradient stops in the specified format
  3620. *
  3621. * @function Highcharts.Color#get
  3622. *
  3623. * @param {string} [format]
  3624. * Possible values are 'a', 'rgb', 'rgba' (default).
  3625. *
  3626. * @return {Highcharts.ColorType}
  3627. * This color as a string or gradient stops.
  3628. */
  3629. get(format) {
  3630. const input = this.input, rgba = this.rgba;
  3631. if (typeof input === 'object' &&
  3632. typeof this.stops !== 'undefined') {
  3633. const ret = merge(input);
  3634. ret.stops = [].slice.call(ret.stops);
  3635. this.stops.forEach((stop, i) => {
  3636. ret.stops[i] = [
  3637. ret.stops[i][0],
  3638. stop.get(format)
  3639. ];
  3640. });
  3641. return ret;
  3642. }
  3643. // it's NaN if gradient colors on a column chart
  3644. if (rgba && isNumber(rgba[0])) {
  3645. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  3646. return 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  3647. }
  3648. if (format === 'a') {
  3649. return `${rgba[3]}`;
  3650. }
  3651. return 'rgba(' + rgba.join(',') + ')';
  3652. }
  3653. return input;
  3654. }
  3655. /**
  3656. * Brighten the color instance.
  3657. *
  3658. * @function Highcharts.Color#brighten
  3659. *
  3660. * @param {number} alpha
  3661. * The alpha value.
  3662. *
  3663. * @return {Highcharts.Color}
  3664. * This color with modifications.
  3665. */
  3666. brighten(alpha) {
  3667. const rgba = this.rgba;
  3668. if (this.stops) {
  3669. this.stops.forEach(function (stop) {
  3670. stop.brighten(alpha);
  3671. });
  3672. }
  3673. else if (isNumber(alpha) && alpha !== 0) {
  3674. for (let i = 0; i < 3; i++) {
  3675. rgba[i] += pInt(alpha * 255);
  3676. if (rgba[i] < 0) {
  3677. rgba[i] = 0;
  3678. }
  3679. if (rgba[i] > 255) {
  3680. rgba[i] = 255;
  3681. }
  3682. }
  3683. }
  3684. return this;
  3685. }
  3686. /**
  3687. * Set the color's opacity to a given alpha value.
  3688. *
  3689. * @function Highcharts.Color#setOpacity
  3690. *
  3691. * @param {number} alpha
  3692. * Opacity between 0 and 1.
  3693. *
  3694. * @return {Highcharts.Color}
  3695. * Color with modifications.
  3696. */
  3697. setOpacity(alpha) {
  3698. this.rgba[3] = alpha;
  3699. return this;
  3700. }
  3701. /**
  3702. * Return an intermediate color between two colors.
  3703. *
  3704. * @function Highcharts.Color#tweenTo
  3705. *
  3706. * @param {Highcharts.Color} to
  3707. * The color object to tween to.
  3708. *
  3709. * @param {number} pos
  3710. * The intermediate position, where 0 is the from color (current color
  3711. * item), and 1 is the `to` color.
  3712. *
  3713. * @return {Highcharts.ColorType}
  3714. * The intermediate color in rgba notation, or unsupported type.
  3715. */
  3716. tweenTo(to, pos) {
  3717. const fromRgba = this.rgba, toRgba = to.rgba;
  3718. // Unsupported color, return to-color (#3920, #7034)
  3719. if (!isNumber(fromRgba[0]) || !isNumber(toRgba[0])) {
  3720. return to.input || 'none';
  3721. }
  3722. // Check for has alpha, because rgba colors perform worse due to
  3723. // lack of support in WebKit.
  3724. const hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  3725. return (hasAlpha ? 'rgba(' : 'rgb(') +
  3726. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  3727. ',' +
  3728. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  3729. ',' +
  3730. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  3731. (hasAlpha ?
  3732. (',' +
  3733. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
  3734. '') +
  3735. ')';
  3736. }
  3737. }
  3738. /* *
  3739. *
  3740. * Static Properties
  3741. *
  3742. * */
  3743. /**
  3744. * Collection of named colors. Can be extended from the outside by adding
  3745. * colors to Highcharts.Color.names.
  3746. * @private
  3747. */
  3748. Color.names = {
  3749. white: '#ffffff',
  3750. black: '#000000'
  3751. };
  3752. /**
  3753. * Collection of parsers. This can be extended from the outside by pushing
  3754. * parsers to `Color.parsers`.
  3755. */
  3756. Color.parsers = [{
  3757. // RGBA color
  3758. // eslint-disable-next-line max-len
  3759. regex: /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,
  3760. parse: function (result) {
  3761. return [
  3762. pInt(result[1]),
  3763. pInt(result[2]),
  3764. pInt(result[3]),
  3765. parseFloat(result[4], 10)
  3766. ];
  3767. }
  3768. }, {
  3769. // RGB color
  3770. regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  3771. parse: function (result) {
  3772. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  3773. }
  3774. }];
  3775. // Must be last static member for init cycle
  3776. Color.None = new Color('');
  3777. /* *
  3778. *
  3779. * Default Export
  3780. *
  3781. * */
  3782. /* *
  3783. *
  3784. * API Declarations
  3785. *
  3786. * */
  3787. /**
  3788. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  3789. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  3790. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  3791. * browsers and displayed correctly, but Highcharts is not able to process them
  3792. * and apply concepts like opacity and brightening.
  3793. *
  3794. * @typedef {string} Highcharts.ColorString
  3795. */
  3796. /**
  3797. * A valid color type than can be parsed and handled by Highcharts. It can be a
  3798. * color string, a gradient object, or a pattern object.
  3799. *
  3800. * @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
  3801. */
  3802. /**
  3803. * Gradient options instead of a solid color.
  3804. *
  3805. * @example
  3806. * // Linear gradient used as a color option
  3807. * color: {
  3808. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  3809. * stops: [
  3810. * [0, '#003399'], // start
  3811. * [0.5, '#ffffff'], // middle
  3812. * [1, '#3366AA'] // end
  3813. * ]
  3814. * }
  3815. *
  3816. * @interface Highcharts.GradientColorObject
  3817. */ /**
  3818. * Holds an object that defines the start position and the end position relative
  3819. * to the shape.
  3820. * @name Highcharts.GradientColorObject#linearGradient
  3821. * @type {Highcharts.LinearGradientColorObject|undefined}
  3822. */ /**
  3823. * Holds an object that defines the center position and the radius.
  3824. * @name Highcharts.GradientColorObject#radialGradient
  3825. * @type {Highcharts.RadialGradientColorObject|undefined}
  3826. */ /**
  3827. * The first item in each tuple is the position in the gradient, where 0 is the
  3828. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  3829. * applied. The second item is the color for each stop. This color can also be
  3830. * given in the rgba format.
  3831. * @name Highcharts.GradientColorObject#stops
  3832. * @type {Array<Highcharts.GradientColorStopObject>}
  3833. */
  3834. /**
  3835. * Color stop tuple.
  3836. *
  3837. * @see Highcharts.GradientColorObject
  3838. *
  3839. * @interface Highcharts.GradientColorStopObject
  3840. */ /**
  3841. * @name Highcharts.GradientColorStopObject#0
  3842. * @type {number}
  3843. */ /**
  3844. * @name Highcharts.GradientColorStopObject#1
  3845. * @type {Highcharts.ColorString}
  3846. */ /**
  3847. * @name Highcharts.GradientColorStopObject#color
  3848. * @type {Highcharts.Color|undefined}
  3849. */
  3850. /**
  3851. * Defines the start position and the end position for a gradient relative
  3852. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  3853. * to the shape, where 0 means top/left and 1 is bottom/right.
  3854. *
  3855. * @interface Highcharts.LinearGradientColorObject
  3856. */ /**
  3857. * Start horizontal position of the gradient. Float ranges 0-1.
  3858. * @name Highcharts.LinearGradientColorObject#x1
  3859. * @type {number}
  3860. */ /**
  3861. * End horizontal position of the gradient. Float ranges 0-1.
  3862. * @name Highcharts.LinearGradientColorObject#x2
  3863. * @type {number}
  3864. */ /**
  3865. * Start vertical position of the gradient. Float ranges 0-1.
  3866. * @name Highcharts.LinearGradientColorObject#y1
  3867. * @type {number}
  3868. */ /**
  3869. * End vertical position of the gradient. Float ranges 0-1.
  3870. * @name Highcharts.LinearGradientColorObject#y2
  3871. * @type {number}
  3872. */
  3873. /**
  3874. * Defines the center position and the radius for a gradient.
  3875. *
  3876. * @interface Highcharts.RadialGradientColorObject
  3877. */ /**
  3878. * Center horizontal position relative to the shape. Float ranges 0-1.
  3879. * @name Highcharts.RadialGradientColorObject#cx
  3880. * @type {number}
  3881. */ /**
  3882. * Center vertical position relative to the shape. Float ranges 0-1.
  3883. * @name Highcharts.RadialGradientColorObject#cy
  3884. * @type {number}
  3885. */ /**
  3886. * Radius relative to the shape. Float ranges 0-1.
  3887. * @name Highcharts.RadialGradientColorObject#r
  3888. * @type {number}
  3889. */
  3890. /**
  3891. * Creates a color instance out of a color string.
  3892. *
  3893. * @function Highcharts.color
  3894. *
  3895. * @param {Highcharts.ColorType} input
  3896. * The input color in either rbga or hex format
  3897. *
  3898. * @return {Highcharts.Color}
  3899. * Color instance
  3900. */
  3901. (''); // detach doclets above
  3902. return Color;
  3903. });
  3904. _registerModule(_modules, 'Core/Color/Palettes.js', [], function () {
  3905. /**
  3906. * Series palettes for Highcharts. Series colors are defined in highcharts.css.
  3907. * **Do not edit this file!** This file is generated using the 'gulp palette' task.
  3908. */
  3909. const SeriesPalettes = {
  3910. /**
  3911. * Colors for data series and points
  3912. */
  3913. colors: [
  3914. '#2caffe',
  3915. '#544fc5',
  3916. '#00e272',
  3917. '#fe6a35',
  3918. '#6b8abc',
  3919. '#d568fb',
  3920. '#2ee0ca',
  3921. '#fa4b42',
  3922. '#feb56a',
  3923. '#91e8e1'
  3924. ]
  3925. };
  3926. return SeriesPalettes;
  3927. });
  3928. _registerModule(_modules, 'Core/Time.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  3929. /* *
  3930. *
  3931. * (c) 2010-2021 Torstein Honsi
  3932. *
  3933. * License: www.highcharts.com/license
  3934. *
  3935. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3936. *
  3937. * */
  3938. const { win } = H;
  3939. const { defined, error, extend, isObject, merge, objectEach, pad, pick, splat, timeUnits } = U;
  3940. /* *
  3941. *
  3942. * Constants
  3943. *
  3944. * */
  3945. const hasNewSafariBug = H.isSafari &&
  3946. win.Intl &&
  3947. win.Intl.DateTimeFormat.prototype.formatRange;
  3948. // To do: Remove this when we no longer need support for Safari < v14.1
  3949. const hasOldSafariBug = H.isSafari &&
  3950. win.Intl &&
  3951. !win.Intl.DateTimeFormat.prototype.formatRange;
  3952. /* *
  3953. *
  3954. * Class
  3955. *
  3956. * */
  3957. /* eslint-disable no-invalid-this, valid-jsdoc */
  3958. /**
  3959. * The Time class. Time settings are applied in general for each page using
  3960. * `Highcharts.setOptions`, or individually for each Chart item through the
  3961. * [time](https://api.highcharts.com/highcharts/time) options set.
  3962. *
  3963. * The Time object is available from {@link Highcharts.Chart#time},
  3964. * which refers to `Highcharts.time` if no individual time settings are
  3965. * applied.
  3966. *
  3967. * @example
  3968. * // Apply time settings globally
  3969. * Highcharts.setOptions({
  3970. * time: {
  3971. * timezone: 'Europe/London'
  3972. * }
  3973. * });
  3974. *
  3975. * // Apply time settings by instance
  3976. * let chart = Highcharts.chart('container', {
  3977. * time: {
  3978. * timezone: 'America/New_York'
  3979. * },
  3980. * series: [{
  3981. * data: [1, 4, 3, 5]
  3982. * }]
  3983. * });
  3984. *
  3985. * // Use the Time object
  3986. * console.log(
  3987. * 'Current time in New York',
  3988. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  3989. * );
  3990. *
  3991. * @since 6.0.5
  3992. *
  3993. * @class
  3994. * @name Highcharts.Time
  3995. *
  3996. * @param {Highcharts.TimeOptions} [options]
  3997. * Time options as defined in [chart.options.time](/highcharts/time).
  3998. */
  3999. class Time {
  4000. /* *
  4001. *
  4002. * Constructors
  4003. *
  4004. * */
  4005. constructor(options) {
  4006. /* *
  4007. *
  4008. * Properties
  4009. *
  4010. * */
  4011. this.options = {};
  4012. this.useUTC = false;
  4013. this.variableTimezone = false;
  4014. this.Date = win.Date;
  4015. /**
  4016. * Get the time zone offset based on the current timezone information as
  4017. * set in the global options.
  4018. *
  4019. * @function Highcharts.Time#getTimezoneOffset
  4020. *
  4021. * @param {number} timestamp
  4022. * The JavaScript timestamp to inspect.
  4023. *
  4024. * @return {number}
  4025. * The timezone offset in minutes compared to UTC.
  4026. */
  4027. this.getTimezoneOffset = this.timezoneOffsetFunction();
  4028. this.update(options);
  4029. }
  4030. /* *
  4031. *
  4032. * Functions
  4033. *
  4034. * */
  4035. /**
  4036. * Time units used in `Time.get` and `Time.set`
  4037. *
  4038. * @typedef {"Date"|"Day"|"FullYear"|"Hours"|"Milliseconds"|"Minutes"|"Month"|"Seconds"} Highcharts.TimeUnitValue
  4039. */
  4040. /**
  4041. * Get the value of a date object in given units, and subject to the Time
  4042. * object's current timezone settings. This function corresponds directly to
  4043. * JavaScripts `Date.getXXX / Date.getUTCXXX`, so instead of calling
  4044. * `date.getHours()` or `date.getUTCHours()` we will call
  4045. * `time.get('Hours')`.
  4046. *
  4047. * @function Highcharts.Time#get
  4048. *
  4049. * @param {Highcharts.TimeUnitValue} unit
  4050. * @param {Date} date
  4051. *
  4052. * @return {number}
  4053. * The given time unit
  4054. */
  4055. get(unit, date) {
  4056. if (this.variableTimezone || this.timezoneOffset) {
  4057. const realMs = date.getTime();
  4058. const ms = realMs - this.getTimezoneOffset(date);
  4059. date.setTime(ms); // Temporary adjust to timezone
  4060. const ret = date['getUTC' + unit]();
  4061. date.setTime(realMs); // Reset
  4062. return ret;
  4063. }
  4064. // UTC time with no timezone handling
  4065. if (this.useUTC) {
  4066. return date['getUTC' + unit]();
  4067. }
  4068. // Else, local time
  4069. return date['get' + unit]();
  4070. }
  4071. /**
  4072. * Set the value of a date object in given units, and subject to the Time
  4073. * object's current timezone settings. This function corresponds directly to
  4074. * JavaScripts `Date.setXXX / Date.setUTCXXX`, so instead of calling
  4075. * `date.setHours(0)` or `date.setUTCHours(0)` we will call
  4076. * `time.set('Hours', 0)`.
  4077. *
  4078. * @function Highcharts.Time#set
  4079. *
  4080. * @param {Highcharts.TimeUnitValue} unit
  4081. * @param {Date} date
  4082. * @param {number} value
  4083. *
  4084. * @return {number}
  4085. * The epoch milliseconds of the updated date
  4086. */
  4087. set(unit, date, value) {
  4088. // UTC time with timezone handling
  4089. if (this.variableTimezone || this.timezoneOffset) {
  4090. // For lower order time units, just set it directly using UTC
  4091. // time
  4092. if (unit === 'Milliseconds' ||
  4093. unit === 'Seconds' ||
  4094. (unit === 'Minutes' &&
  4095. this.getTimezoneOffset(date) % 3600000 === 0) // #13961
  4096. ) {
  4097. return date['setUTC' + unit](value);
  4098. }
  4099. // Higher order time units need to take the time zone into
  4100. // account
  4101. // Adjust by timezone
  4102. const offset = this.getTimezoneOffset(date);
  4103. let ms = date.getTime() - offset;
  4104. date.setTime(ms);
  4105. date['setUTC' + unit](value);
  4106. const newOffset = this.getTimezoneOffset(date);
  4107. ms = date.getTime() + newOffset;
  4108. return date.setTime(ms);
  4109. }
  4110. // UTC time with no timezone handling
  4111. if (this.useUTC ||
  4112. // leap calculation in UTC only
  4113. (hasNewSafariBug && unit === 'FullYear')) {
  4114. return date['setUTC' + unit](value);
  4115. }
  4116. // Else, local time
  4117. return date['set' + unit](value);
  4118. }
  4119. /**
  4120. * Update the Time object with current options. It is called internally on
  4121. * initializing Highcharts, after running `Highcharts.setOptions` and on
  4122. * `Chart.update`.
  4123. *
  4124. * @private
  4125. * @function Highcharts.Time#update
  4126. *
  4127. * @param {Highcharts.TimeOptions} [options]
  4128. *
  4129. */
  4130. update(options = {}) {
  4131. const useUTC = pick(options.useUTC, true);
  4132. this.options = options = merge(true, this.options, options);
  4133. // Allow using a different Date class
  4134. this.Date = options.Date || win.Date || Date;
  4135. this.useUTC = useUTC;
  4136. this.timezoneOffset = (useUTC && options.timezoneOffset) || void 0;
  4137. this.getTimezoneOffset = this.timezoneOffsetFunction();
  4138. /*
  4139. * The time object has options allowing for variable time zones, meaning
  4140. * the axis ticks or series data needs to consider this.
  4141. */
  4142. this.variableTimezone = useUTC && !!(options.getTimezoneOffset ||
  4143. options.timezone);
  4144. }
  4145. /**
  4146. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  4147. * local time or a specific timezone time depending on the current time
  4148. * settings.
  4149. *
  4150. * @function Highcharts.Time#makeTime
  4151. *
  4152. * @param {number} year
  4153. * The year
  4154. *
  4155. * @param {number} month
  4156. * The month. Zero-based, so January is 0.
  4157. *
  4158. * @param {number} [date=1]
  4159. * The day of the month
  4160. *
  4161. * @param {number} [hours=0]
  4162. * The hour of the day, 0-23.
  4163. *
  4164. * @param {number} [minutes=0]
  4165. * The minutes
  4166. *
  4167. * @param {number} [seconds=0]
  4168. * The seconds
  4169. *
  4170. * @return {number}
  4171. * The time in milliseconds since January 1st 1970.
  4172. */
  4173. makeTime(year, month, date, hours, minutes, seconds) {
  4174. let d, offset, newOffset;
  4175. if (this.useUTC) {
  4176. d = this.Date.UTC.apply(0, arguments);
  4177. offset = this.getTimezoneOffset(d);
  4178. d += offset;
  4179. newOffset = this.getTimezoneOffset(d);
  4180. if (offset !== newOffset) {
  4181. d += newOffset - offset;
  4182. // A special case for transitioning from summer time to winter time.
  4183. // When the clock is set back, the same time is repeated twice, i.e.
  4184. // 02:30 am is repeated since the clock is set back from 3 am to
  4185. // 2 am. We need to make the same time as local Date does.
  4186. }
  4187. else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  4188. !hasOldSafariBug) {
  4189. d -= 36e5;
  4190. }
  4191. }
  4192. else {
  4193. d = new this.Date(year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0)).getTime();
  4194. }
  4195. return d;
  4196. }
  4197. /**
  4198. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  4199. * default getTimezoneOffset function with that timezone is returned. If
  4200. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  4201. * specified, the function using the `timezoneOffset` option or 0 offset is
  4202. * returned.
  4203. *
  4204. * @private
  4205. * @function Highcharts.Time#timezoneOffsetFunction
  4206. *
  4207. * @return {Function}
  4208. * A getTimezoneOffset function
  4209. */
  4210. timezoneOffsetFunction() {
  4211. const time = this, options = this.options, getTimezoneOffset = options.getTimezoneOffset, moment = options.moment || win.moment;
  4212. if (!this.useUTC) {
  4213. return function (timestamp) {
  4214. return new Date(timestamp.toString()).getTimezoneOffset() * 60000;
  4215. };
  4216. }
  4217. if (options.timezone) {
  4218. if (!moment) {
  4219. // getTimezoneOffset-function stays undefined because it depends
  4220. // on Moment.js
  4221. error(25);
  4222. }
  4223. else {
  4224. return function (timestamp) {
  4225. return -moment.tz(timestamp, options.timezone).utcOffset() * 60000;
  4226. };
  4227. }
  4228. }
  4229. // If not timezone is set, look for the getTimezoneOffset callback
  4230. if (this.useUTC && getTimezoneOffset) {
  4231. return function (timestamp) {
  4232. return getTimezoneOffset(timestamp.valueOf()) * 60000;
  4233. };
  4234. }
  4235. // Last, use the `timezoneOffset` option if set
  4236. return function () {
  4237. return (time.timezoneOffset || 0) * 60000;
  4238. };
  4239. }
  4240. /**
  4241. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  4242. * into a human readable date string. The available format keys are listed
  4243. * below. Additional formats can be given in the
  4244. * {@link Highcharts.dateFormats} hook.
  4245. *
  4246. * Supported format keys:
  4247. * - `%a`: Short weekday, like 'Mon'
  4248. * - `%A`: Long weekday, like 'Monday'
  4249. * - `%d`: Two digit day of the month, 01 to 31
  4250. * - `%e`: Day of the month, 1 through 31
  4251. * - `%w`: Day of the week, 0 through 6
  4252. * - `%b`: Short month, like 'Jan'
  4253. * - `%B`: Long month, like 'January'
  4254. * - `%m`: Two digit month number, 01 through 12
  4255. * - `%y`: Two digits year, like 09 for 2009
  4256. * - `%Y`: Four digits year, like 2009
  4257. * - `%H`: Two digits hours in 24h format, 00 through 23
  4258. * - `%k`: Hours in 24h format, 0 through 23
  4259. * - `%I`: Two digits hours in 12h format, 00 through 11
  4260. * - `%l`: Hours in 12h format, 1 through 12
  4261. * - `%M`: Two digits minutes, 00 through 59
  4262. * - `%p`: Upper case AM or PM
  4263. * - `%P`: Lower case AM or PM
  4264. * - `%S`: Two digits seconds, 00 through 59
  4265. * - `%L`: Milliseconds (naming from Ruby)
  4266. *
  4267. * @example
  4268. * const time = new Highcharts.Time();
  4269. * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
  4270. * console.log(s); // => 2020-01-01 00:00:00
  4271. *
  4272. * @function Highcharts.Time#dateFormat
  4273. *
  4274. * @param {string} format
  4275. * The desired format where various time representations are
  4276. * prefixed with %.
  4277. *
  4278. * @param {number} [timestamp]
  4279. * The JavaScript timestamp.
  4280. *
  4281. * @param {boolean} [capitalize=false]
  4282. * Upper case first letter in the return.
  4283. *
  4284. * @return {string}
  4285. * The formatted date.
  4286. */
  4287. dateFormat(format, timestamp, capitalize) {
  4288. if (!defined(timestamp) || isNaN(timestamp)) {
  4289. return (H.defaultOptions.lang &&
  4290. H.defaultOptions.lang.invalidDate ||
  4291. '');
  4292. }
  4293. format = pick(format, '%Y-%m-%d %H:%M:%S');
  4294. const time = this, date = new this.Date(timestamp),
  4295. // get the basic time values
  4296. hours = this.get('Hours', date), day = this.get('Day', date), dayOfMonth = this.get('Date', date), month = this.get('Month', date), fullYear = this.get('FullYear', date), lang = H.defaultOptions.lang, langWeekdays = (lang && lang.weekdays), shortWeekdays = (lang && lang.shortWeekdays),
  4297. // List all format keys. Custom formats can be added from the
  4298. // outside.
  4299. replacements = extend({
  4300. // Day
  4301. // Short weekday, like 'Mon'
  4302. a: shortWeekdays ?
  4303. shortWeekdays[day] :
  4304. langWeekdays[day].substr(0, 3),
  4305. // Long weekday, like 'Monday'
  4306. A: langWeekdays[day],
  4307. // Two digit day of the month, 01 to 31
  4308. d: pad(dayOfMonth),
  4309. // Day of the month, 1 through 31
  4310. e: pad(dayOfMonth, 2, ' '),
  4311. // Day of the week, 0 through 6
  4312. w: day,
  4313. // Week (none implemented)
  4314. // 'W': weekNumber(),
  4315. // Month
  4316. // Short month, like 'Jan'
  4317. b: lang.shortMonths[month],
  4318. // Long month, like 'January'
  4319. B: lang.months[month],
  4320. // Two digit month number, 01 through 12
  4321. m: pad(month + 1),
  4322. // Month number, 1 through 12 (#8150)
  4323. o: month + 1,
  4324. // Year
  4325. // Two digits year, like 09 for 2009
  4326. y: fullYear.toString().substr(2, 2),
  4327. // Four digits year, like 2009
  4328. Y: fullYear,
  4329. // Time
  4330. // Two digits hours in 24h format, 00 through 23
  4331. H: pad(hours),
  4332. // Hours in 24h format, 0 through 23
  4333. k: hours,
  4334. // Two digits hours in 12h format, 00 through 11
  4335. I: pad((hours % 12) || 12),
  4336. // Hours in 12h format, 1 through 12
  4337. l: (hours % 12) || 12,
  4338. // Two digits minutes, 00 through 59
  4339. M: pad(this.get('Minutes', date)),
  4340. // Upper case AM or PM
  4341. p: hours < 12 ? 'AM' : 'PM',
  4342. // Lower case AM or PM
  4343. P: hours < 12 ? 'am' : 'pm',
  4344. // Two digits seconds, 00 through 59
  4345. S: pad(date.getSeconds()),
  4346. // Milliseconds (naming from Ruby)
  4347. L: pad(Math.floor(timestamp % 1000), 3)
  4348. }, H.dateFormats);
  4349. // Do the replaces
  4350. objectEach(replacements, function (val, key) {
  4351. // Regex would do it in one line, but this is faster
  4352. while (format.indexOf('%' + key) !== -1) {
  4353. format = format.replace('%' + key, typeof val === 'function' ? val.call(time, timestamp) : val);
  4354. }
  4355. });
  4356. // Optionally capitalize the string and return
  4357. return capitalize ?
  4358. (format.substr(0, 1).toUpperCase() +
  4359. format.substr(1)) :
  4360. format;
  4361. }
  4362. /**
  4363. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  4364. * an object.
  4365. * @private
  4366. * @param {string|Array<T>|Highcharts.Dictionary<T>} f
  4367. * General format description
  4368. * @return {Highcharts.Dictionary<T>}
  4369. * The object definition
  4370. */
  4371. resolveDTLFormat(f) {
  4372. if (!isObject(f, true)) { // check for string or array
  4373. f = splat(f);
  4374. return {
  4375. main: f[0],
  4376. from: f[1],
  4377. to: f[2]
  4378. };
  4379. }
  4380. return f;
  4381. }
  4382. /**
  4383. * Return an array with time positions distributed on round time values
  4384. * right and right after min and max. Used in datetime axes as well as for
  4385. * grouping data on a datetime axis.
  4386. *
  4387. * @function Highcharts.Time#getTimeTicks
  4388. *
  4389. * @param {Highcharts.TimeNormalizedObject} normalizedInterval
  4390. * The interval in axis values (ms) and the count
  4391. *
  4392. * @param {number} [min]
  4393. * The minimum in axis values
  4394. *
  4395. * @param {number} [max]
  4396. * The maximum in axis values
  4397. *
  4398. * @param {number} [startOfWeek=1]
  4399. *
  4400. * @return {Highcharts.AxisTickPositionsArray}
  4401. * Time positions
  4402. */
  4403. getTimeTicks(normalizedInterval, min, max, startOfWeek) {
  4404. const time = this, Date = time.Date, tickPositions = [], higherRanks = {},
  4405. // When crossing DST, use the max. Resolves #6278.
  4406. minDate = new Date(min), interval = normalizedInterval.unitRange, count = normalizedInterval.count || 1;
  4407. let i, minYear, // used in months and years as a basis for Date.UTC()
  4408. variableDayLength, minDay;
  4409. startOfWeek = pick(startOfWeek, 1);
  4410. if (defined(min)) { // #1300
  4411. time.set('Milliseconds', minDate, interval >= timeUnits.second ?
  4412. 0 : // #3935
  4413. count * Math.floor(time.get('Milliseconds', minDate) / count)); // #3652, #3654
  4414. if (interval >= timeUnits.second) { // second
  4415. time.set('Seconds', minDate, interval >= timeUnits.minute ?
  4416. 0 : // #3935
  4417. count * Math.floor(time.get('Seconds', minDate) / count));
  4418. }
  4419. if (interval >= timeUnits.minute) { // minute
  4420. time.set('Minutes', minDate, interval >= timeUnits.hour ?
  4421. 0 :
  4422. count * Math.floor(time.get('Minutes', minDate) / count));
  4423. }
  4424. if (interval >= timeUnits.hour) { // hour
  4425. time.set('Hours', minDate, interval >= timeUnits.day ?
  4426. 0 :
  4427. count * Math.floor(time.get('Hours', minDate) / count));
  4428. }
  4429. if (interval >= timeUnits.day) { // day
  4430. time.set('Date', minDate, interval >= timeUnits.month ?
  4431. 1 :
  4432. Math.max(1, count * Math.floor(time.get('Date', minDate) / count)));
  4433. }
  4434. if (interval >= timeUnits.month) { // month
  4435. time.set('Month', minDate, interval >= timeUnits.year ? 0 :
  4436. count * Math.floor(time.get('Month', minDate) / count));
  4437. minYear = time.get('FullYear', minDate);
  4438. }
  4439. if (interval >= timeUnits.year) { // year
  4440. minYear -= minYear % count;
  4441. time.set('FullYear', minDate, minYear);
  4442. }
  4443. // week is a special case that runs outside the hierarchy
  4444. if (interval === timeUnits.week) {
  4445. // get start of current week, independent of count
  4446. minDay = time.get('Day', minDate);
  4447. time.set('Date', minDate, (time.get('Date', minDate) -
  4448. minDay + startOfWeek +
  4449. // We don't want to skip days that are before
  4450. // startOfWeek (#7051)
  4451. (minDay < startOfWeek ? -7 : 0)));
  4452. }
  4453. // Get basics for variable time spans
  4454. minYear = time.get('FullYear', minDate);
  4455. const minMonth = time.get('Month', minDate), minDateDate = time.get('Date', minDate), minHours = time.get('Hours', minDate);
  4456. // Redefine min to the floored/rounded minimum time (#7432)
  4457. min = minDate.getTime();
  4458. // Handle local timezone offset
  4459. if ((time.variableTimezone || !time.useUTC) && defined(max)) {
  4460. // Detect whether we need to take the DST crossover into
  4461. // consideration. If we're crossing over DST, the day length may
  4462. // be 23h or 25h and we need to compute the exact clock time for
  4463. // each tick instead of just adding hours. This comes at a cost,
  4464. // so first we find out if it is needed (#4951).
  4465. variableDayLength = (
  4466. // Long range, assume we're crossing over.
  4467. max - min > 4 * timeUnits.month ||
  4468. // Short range, check if min and max are in different time
  4469. // zones.
  4470. time.getTimezoneOffset(min) !==
  4471. time.getTimezoneOffset(max));
  4472. }
  4473. // Iterate and add tick positions at appropriate values
  4474. let t = minDate.getTime();
  4475. i = 1;
  4476. while (t < max) {
  4477. tickPositions.push(t);
  4478. // if the interval is years, use Date.UTC to increase years
  4479. if (interval === timeUnits.year) {
  4480. t = time.makeTime(minYear + i * count, 0);
  4481. // if the interval is months, use Date.UTC to increase months
  4482. }
  4483. else if (interval === timeUnits.month) {
  4484. t = time.makeTime(minYear, minMonth + i * count);
  4485. // if we're using global time, the interval is not fixed as it
  4486. // jumps one hour at the DST crossover
  4487. }
  4488. else if (variableDayLength &&
  4489. (interval === timeUnits.day || interval === timeUnits.week)) {
  4490. t = time.makeTime(minYear, minMonth, minDateDate +
  4491. i * count * (interval === timeUnits.day ? 1 : 7));
  4492. }
  4493. else if (variableDayLength &&
  4494. interval === timeUnits.hour &&
  4495. count > 1) {
  4496. // make sure higher ranks are preserved across DST (#6797,
  4497. // #7621)
  4498. t = time.makeTime(minYear, minMonth, minDateDate, minHours + i * count);
  4499. // else, the interval is fixed and we use simple addition
  4500. }
  4501. else {
  4502. t += interval * count;
  4503. }
  4504. i++;
  4505. }
  4506. // push the last time
  4507. tickPositions.push(t);
  4508. // Handle higher ranks. Mark new days if the time is on midnight
  4509. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  4510. // to prevent looping over dense data grouping (#6156).
  4511. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  4512. tickPositions.forEach(function (t) {
  4513. if (
  4514. // Speed optimization, no need to run dateFormat unless
  4515. // we're on a full or half hour
  4516. t % 1800000 === 0 &&
  4517. // Check for local or global midnight
  4518. time.dateFormat('%H%M%S%L', t) === '000000000') {
  4519. higherRanks[t] = 'day';
  4520. }
  4521. });
  4522. }
  4523. }
  4524. // record information on the chosen unit - for dynamic label formatter
  4525. tickPositions.info = extend(normalizedInterval, {
  4526. higherRanks,
  4527. totalRange: interval * count
  4528. });
  4529. return tickPositions;
  4530. }
  4531. /**
  4532. * Get the optimal date format for a point, based on a range.
  4533. *
  4534. * @private
  4535. * @function Highcharts.Time#getDateFormat
  4536. *
  4537. * @param {number} range
  4538. * The time range
  4539. *
  4540. * @param {number} timestamp
  4541. * The timestamp of the date
  4542. *
  4543. * @param {number} startOfWeek
  4544. * An integer representing the first day of the week, where 0 is
  4545. * Sunday.
  4546. *
  4547. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  4548. * A map of time units to formats.
  4549. *
  4550. * @return {string}
  4551. * The optimal date format for a point.
  4552. */
  4553. getDateFormat(range, timestamp, startOfWeek, dateTimeLabelFormats) {
  4554. const dateStr = this.dateFormat('%m-%d %H:%M:%S.%L', timestamp), blank = '01-01 00:00:00.000', strpos = {
  4555. millisecond: 15,
  4556. second: 12,
  4557. minute: 9,
  4558. hour: 6,
  4559. day: 3
  4560. };
  4561. let n = 'millisecond',
  4562. // for sub-millisecond data, #4223
  4563. lastN = n;
  4564. for (n in timeUnits) { // eslint-disable-line guard-for-in
  4565. // If the range is exactly one week and we're looking at a
  4566. // Sunday/Monday, go for the week format
  4567. if (range === timeUnits.week &&
  4568. +this.dateFormat('%w', timestamp) === startOfWeek &&
  4569. dateStr.substr(6) === blank.substr(6)) {
  4570. n = 'week';
  4571. break;
  4572. }
  4573. // The first format that is too great for the range
  4574. if (timeUnits[n] > range) {
  4575. n = lastN;
  4576. break;
  4577. }
  4578. // If the point is placed every day at 23:59, we need to show
  4579. // the minutes as well. #2637.
  4580. if (strpos[n] &&
  4581. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
  4582. break;
  4583. }
  4584. // Weeks are outside the hierarchy, only apply them on
  4585. // Mondays/Sundays like in the first condition
  4586. if (n !== 'week') {
  4587. lastN = n;
  4588. }
  4589. }
  4590. return this.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  4591. }
  4592. }
  4593. /* *
  4594. *
  4595. * Default export
  4596. *
  4597. * */
  4598. /* *
  4599. *
  4600. * API Declarations
  4601. *
  4602. * */
  4603. /**
  4604. * Normalized interval.
  4605. *
  4606. * @interface Highcharts.TimeNormalizedObject
  4607. */ /**
  4608. * The count.
  4609. *
  4610. * @name Highcharts.TimeNormalizedObject#count
  4611. * @type {number|undefined}
  4612. */ /**
  4613. * The interval in axis values (ms).
  4614. *
  4615. * @name Highcharts.TimeNormalizedObject#unitRange
  4616. * @type {number}
  4617. */
  4618. /**
  4619. * Function of an additional date format specifier.
  4620. *
  4621. * @callback Highcharts.TimeFormatCallbackFunction
  4622. *
  4623. * @param {number} timestamp
  4624. * The time to format.
  4625. *
  4626. * @return {string}
  4627. * The formatted portion of the date.
  4628. */
  4629. /**
  4630. * Time ticks.
  4631. *
  4632. * @interface Highcharts.AxisTickPositionsArray
  4633. * @extends global.Array<number>
  4634. */ /**
  4635. * @name Highcharts.AxisTickPositionsArray#info
  4636. * @type {Highcharts.TimeTicksInfoObject|undefined}
  4637. */
  4638. /**
  4639. * A callback to return the time zone offset for a given datetime. It
  4640. * takes the timestamp in terms of milliseconds since January 1 1970,
  4641. * and returns the timezone offset in minutes. This provides a hook
  4642. * for drawing time based charts in specific time zones using their
  4643. * local DST crossover dates, with the help of external libraries.
  4644. *
  4645. * @callback Highcharts.TimezoneOffsetCallbackFunction
  4646. *
  4647. * @param {number} timestamp
  4648. * Timestamp in terms of milliseconds since January 1 1970.
  4649. *
  4650. * @return {number}
  4651. * Timezone offset in minutes.
  4652. */
  4653. /**
  4654. * Allows to manually load the `moment.js` library from Highcharts options
  4655. * instead of the `window`.
  4656. * In case of loading the library from a `script` tag,
  4657. * this option is not needed, it will be loaded from there by default.
  4658. *
  4659. * @type {Function}
  4660. * @since 8.2.0
  4661. * @apioption time.moment
  4662. */
  4663. ''; // keeps doclets above in JS file
  4664. return Time;
  4665. });
  4666. _registerModule(_modules, 'Core/Defaults.js', [_modules['Core/Chart/ChartDefaults.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palettes.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js']], function (ChartDefaults, Color, H, Palettes, Time, U) {
  4667. /* *
  4668. *
  4669. * (c) 2010-2021 Torstein Honsi
  4670. *
  4671. * License: www.highcharts.com/license
  4672. *
  4673. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4674. *
  4675. * */
  4676. const { parse: color } = Color;
  4677. const { isTouchDevice, svg } = H;
  4678. const { merge } = U;
  4679. /* *
  4680. *
  4681. * API Options
  4682. *
  4683. * */
  4684. /**
  4685. * Global default settings.
  4686. *
  4687. * @name Highcharts.defaultOptions
  4688. * @type {Highcharts.Options}
  4689. */ /**
  4690. * @optionparent
  4691. * @private
  4692. */
  4693. const defaultOptions = {
  4694. /**
  4695. * An array containing the default colors for the chart's series. When
  4696. * all colors are used, new colors are pulled from the start again.
  4697. *
  4698. * Default colors can also be set on a series or series.type basis,
  4699. * see [column.colors](#plotOptions.column.colors),
  4700. * [pie.colors](#plotOptions.pie.colors).
  4701. *
  4702. * In styled mode, the colors option doesn't exist. Instead, colors
  4703. * are defined in CSS and applied either through series or point class
  4704. * names, or through the [chart.colorCount](#chart.colorCount) option.
  4705. *
  4706. * @sample {highcharts} highcharts/chart/colors/
  4707. * Assign a global color theme
  4708. * @sample highcharts/members/theme-v10/
  4709. * Latest release styled like version 10
  4710. *
  4711. * @type {Array<(Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject)>}
  4712. * @default [
  4713. * "#2caffe",
  4714. * "#544fc5",
  4715. * "#00e272",
  4716. * "#fe6a35",
  4717. * "#6b8abc",
  4718. * "#d568fb",
  4719. * "#2ee0ca",
  4720. * "#fa4b42",
  4721. * "#feb56a",
  4722. * "#91e8e12
  4723. * ]
  4724. */
  4725. colors: Palettes.colors,
  4726. /**
  4727. * Styled mode only. Configuration object for adding SVG definitions for
  4728. * reusable elements. See [gradients, shadows and
  4729. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  4730. * for more information and code examples.
  4731. *
  4732. * @type {*}
  4733. * @since 5.0.0
  4734. * @apioption defs
  4735. */
  4736. /**
  4737. * @ignore-option
  4738. */
  4739. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  4740. /**
  4741. * The language object is global and it can't be set on each chart
  4742. * initialization. Instead, use `Highcharts.setOptions` to set it before any
  4743. * chart is initialized.
  4744. *
  4745. * ```js
  4746. * Highcharts.setOptions({
  4747. * lang: {
  4748. * months: [
  4749. * 'Janvier', 'Février', 'Mars', 'Avril',
  4750. * 'Mai', 'Juin', 'Juillet', 'Août',
  4751. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  4752. * ],
  4753. * weekdays: [
  4754. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  4755. * 'Jeudi', 'Vendredi', 'Samedi'
  4756. * ]
  4757. * }
  4758. * });
  4759. * ```
  4760. */
  4761. lang: {
  4762. /**
  4763. * The loading text that appears when the chart is set into the loading
  4764. * state following a call to `chart.showLoading`.
  4765. */
  4766. loading: 'Loading...',
  4767. /**
  4768. * An array containing the months names. Corresponds to the `%B` format
  4769. * in `Highcharts.dateFormat()`.
  4770. *
  4771. * @type {Array<string>}
  4772. * @default ["January", "February", "March", "April", "May", "June",
  4773. * "July", "August", "September", "October", "November",
  4774. * "December"]
  4775. */
  4776. months: [
  4777. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  4778. 'August', 'September', 'October', 'November', 'December'
  4779. ],
  4780. /**
  4781. * An array containing the months names in abbreviated form. Corresponds
  4782. * to the `%b` format in `Highcharts.dateFormat()`.
  4783. *
  4784. * @type {Array<string>}
  4785. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  4786. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  4787. */
  4788. shortMonths: [
  4789. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  4790. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  4791. ],
  4792. /**
  4793. * An array containing the weekday names.
  4794. *
  4795. * @type {Array<string>}
  4796. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  4797. * "Friday", "Saturday"]
  4798. */
  4799. weekdays: [
  4800. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  4801. 'Thursday', 'Friday', 'Saturday'
  4802. ],
  4803. /**
  4804. * Short week days, starting Sunday. If not specified, Highcharts uses
  4805. * the first three letters of the `lang.weekdays` option.
  4806. *
  4807. * @sample highcharts/lang/shortweekdays/
  4808. * Finnish two-letter abbreviations
  4809. *
  4810. * @type {Array<string>}
  4811. * @since 4.2.4
  4812. * @apioption lang.shortWeekdays
  4813. */
  4814. /**
  4815. * What to show in a date field for invalid dates. Defaults to an empty
  4816. * string.
  4817. *
  4818. * @type {string}
  4819. * @since 4.1.8
  4820. * @product highcharts highstock
  4821. * @apioption lang.invalidDate
  4822. */
  4823. /**
  4824. * The title appearing on hovering the zoom in button. The text itself
  4825. * defaults to "+" and can be changed in the button options.
  4826. *
  4827. * @type {string}
  4828. * @default Zoom in
  4829. * @product highmaps
  4830. * @apioption lang.zoomIn
  4831. */
  4832. /**
  4833. * The title appearing on hovering the zoom out button. The text itself
  4834. * defaults to "-" and can be changed in the button options.
  4835. *
  4836. * @type {string}
  4837. * @default Zoom out
  4838. * @product highmaps
  4839. * @apioption lang.zoomOut
  4840. */
  4841. /**
  4842. * The default decimal point used in the `Highcharts.numberFormat`
  4843. * method unless otherwise specified in the function arguments.
  4844. *
  4845. * @since 1.2.2
  4846. */
  4847. decimalPoint: '.',
  4848. /**
  4849. * [Metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) used
  4850. * to shorten high numbers in axis labels. Replacing any of the
  4851. * positions with `null` causes the full number to be written. Setting
  4852. * `numericSymbols` to `null` disables shortening altogether.
  4853. *
  4854. * @sample {highcharts} highcharts/lang/numericsymbols/
  4855. * Replacing the symbols with text
  4856. * @sample {highstock} highcharts/lang/numericsymbols/
  4857. * Replacing the symbols with text
  4858. *
  4859. * @type {Array<string>}
  4860. * @default ["k", "M", "G", "T", "P", "E"]
  4861. * @since 2.3.0
  4862. */
  4863. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  4864. /**
  4865. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  4866. * Use 10000 for Japanese, Korean and various Chinese locales, which
  4867. * use symbols for 10^4, 10^8 and 10^12.
  4868. *
  4869. * @sample highcharts/lang/numericsymbolmagnitude/
  4870. * 10000 magnitude for Japanese
  4871. *
  4872. * @type {number}
  4873. * @default 1000
  4874. * @since 5.0.3
  4875. * @apioption lang.numericSymbolMagnitude
  4876. */
  4877. /**
  4878. * The text for the label appearing when a chart is zoomed.
  4879. *
  4880. * @since 1.2.4
  4881. */
  4882. resetZoom: 'Reset zoom',
  4883. /**
  4884. * The tooltip title for the label appearing when a chart is zoomed.
  4885. *
  4886. * @since 1.2.4
  4887. */
  4888. resetZoomTitle: 'Reset zoom level 1:1',
  4889. /**
  4890. * The default thousands separator used in the `Highcharts.numberFormat`
  4891. * method unless otherwise specified in the function arguments. Defaults
  4892. * to a single space character, which is recommended in
  4893. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  4894. * across Anglo-American and continental European languages.
  4895. *
  4896. * @default \u0020
  4897. * @since 1.2.2
  4898. */
  4899. thousandsSep: ' '
  4900. },
  4901. /**
  4902. * Global options that don't apply to each chart. These options, like
  4903. * the `lang` options, must be set using the `Highcharts.setOptions`
  4904. * method.
  4905. *
  4906. * ```js
  4907. * Highcharts.setOptions({
  4908. * global: {
  4909. * useUTC: false
  4910. * }
  4911. * });
  4912. * ```
  4913. */
  4914. /**
  4915. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  4916. * Use the [libURL](#exporting.libURL) option to configure exporting._
  4917. *
  4918. * The URL to the additional file to lazy load for Android 2.x devices.
  4919. * These devices don't support SVG, so we download a helper file that
  4920. * contains [canvg](https://github.com/canvg/canvg), its dependency
  4921. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  4922. * our site, you can install canvas-tools.js on your own server and
  4923. * change this option accordingly.
  4924. *
  4925. * @deprecated
  4926. *
  4927. * @type {string}
  4928. * @default https://code.highcharts.com/{version}/modules/canvas-tools.js
  4929. * @product highcharts highmaps
  4930. * @apioption global.canvasToolsURL
  4931. */
  4932. /**
  4933. * This option is deprecated since v6.0.5. Instead, use
  4934. * [time.useUTC](#time.useUTC) that supports individual time settings
  4935. * per chart.
  4936. *
  4937. * @deprecated
  4938. *
  4939. * @type {boolean}
  4940. * @apioption global.useUTC
  4941. */
  4942. /**
  4943. * This option is deprecated since v6.0.5. Instead, use
  4944. * [time.Date](#time.Date) that supports individual time settings
  4945. * per chart.
  4946. *
  4947. * @deprecated
  4948. *
  4949. * @type {Function}
  4950. * @product highcharts highstock
  4951. * @apioption global.Date
  4952. */
  4953. /**
  4954. * This option is deprecated since v6.0.5. Instead, use
  4955. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  4956. * individual time settings per chart.
  4957. *
  4958. * @deprecated
  4959. *
  4960. * @type {Function}
  4961. * @product highcharts highstock
  4962. * @apioption global.getTimezoneOffset
  4963. */
  4964. /**
  4965. * This option is deprecated since v6.0.5. Instead, use
  4966. * [time.timezone](#time.timezone) that supports individual time
  4967. * settings per chart.
  4968. *
  4969. * @deprecated
  4970. *
  4971. * @type {string}
  4972. * @product highcharts highstock
  4973. * @apioption global.timezone
  4974. */
  4975. /**
  4976. * This option is deprecated since v6.0.5. Instead, use
  4977. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  4978. * time settings per chart.
  4979. *
  4980. * @deprecated
  4981. *
  4982. * @type {number}
  4983. * @product highcharts highstock
  4984. * @apioption global.timezoneOffset
  4985. */
  4986. global: {},
  4987. /**
  4988. * Time options that can apply globally or to individual charts. These
  4989. * settings affect how `datetime` axes are laid out, how tooltips are
  4990. * formatted, how series
  4991. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  4992. * the Highcharts Stock range selector handles time.
  4993. *
  4994. * The common use case is that all charts in the same Highcharts object
  4995. * share the same time settings, in which case the global settings are set
  4996. * using `setOptions`.
  4997. *
  4998. * ```js
  4999. * // Apply time settings globally
  5000. * Highcharts.setOptions({
  5001. * time: {
  5002. * timezone: 'Europe/London'
  5003. * }
  5004. * });
  5005. * // Apply time settings by instance
  5006. * let chart = Highcharts.chart('container', {
  5007. * time: {
  5008. * timezone: 'America/New_York'
  5009. * },
  5010. * series: [{
  5011. * data: [1, 4, 3, 5]
  5012. * }]
  5013. * });
  5014. *
  5015. * // Use the Time object
  5016. * console.log(
  5017. * 'Current time in New York',
  5018. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  5019. * );
  5020. * ```
  5021. *
  5022. * Since v6.0.5, the time options were moved from the `global` obect to the
  5023. * `time` object, and time options can be set on each individual chart.
  5024. *
  5025. * @sample {highcharts|highstock}
  5026. * highcharts/time/timezone/
  5027. * Set the timezone globally
  5028. * @sample {highcharts}
  5029. * highcharts/time/individual/
  5030. * Set the timezone per chart instance
  5031. * @sample {highstock}
  5032. * stock/time/individual/
  5033. * Set the timezone per chart instance
  5034. *
  5035. * @since 6.0.5
  5036. * @optionparent time
  5037. */
  5038. time: {
  5039. /**
  5040. * A custom `Date` class for advanced date handling. For example,
  5041. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  5042. * handle Jalali dates.
  5043. *
  5044. * @type {*}
  5045. * @since 4.0.4
  5046. * @product highcharts highstock gantt
  5047. */
  5048. Date: void 0,
  5049. /**
  5050. * A callback to return the time zone offset for a given datetime. It
  5051. * takes the timestamp in terms of milliseconds since January 1 1970,
  5052. * and returns the timezone offset in minutes. This provides a hook
  5053. * for drawing time based charts in specific time zones using their
  5054. * local DST crossover dates, with the help of external libraries.
  5055. *
  5056. * @see [global.timezoneOffset](#global.timezoneOffset)
  5057. *
  5058. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  5059. * Use moment.js to draw Oslo time regardless of browser locale
  5060. *
  5061. * @type {Highcharts.TimezoneOffsetCallbackFunction}
  5062. * @since 4.1.0
  5063. * @product highcharts highstock gantt
  5064. */
  5065. getTimezoneOffset: void 0,
  5066. /**
  5067. * Requires [moment.js](https://momentjs.com/). If the timezone option
  5068. * is specified, it creates a default
  5069. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  5070. * up the specified timezone in moment.js. If moment.js is not included,
  5071. * this throws a Highcharts error in the console, but does not crash the
  5072. * chart.
  5073. *
  5074. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  5075. *
  5076. * @sample {highcharts|highstock} highcharts/time/timezone/
  5077. * Europe/Oslo
  5078. *
  5079. * @type {string}
  5080. * @since 5.0.7
  5081. * @product highcharts highstock gantt
  5082. */
  5083. timezone: void 0,
  5084. /**
  5085. * The timezone offset in minutes. Positive values are west, negative
  5086. * values are east of UTC, as in the ECMAScript
  5087. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  5088. * method. Use this to display UTC based data in a predefined time zone.
  5089. *
  5090. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  5091. *
  5092. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  5093. * Timezone offset
  5094. *
  5095. * @since 3.0.8
  5096. * @product highcharts highstock gantt
  5097. */
  5098. timezoneOffset: 0,
  5099. /**
  5100. * Whether to use UTC time for axis scaling, tickmark placement and
  5101. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  5102. * is that the time displays equally regardless of the user agent's
  5103. * time zone settings. Local time can be used when the data is loaded
  5104. * in real time or when correct Daylight Saving Time transitions are
  5105. * required.
  5106. *
  5107. * @sample {highcharts} highcharts/time/useutc-true/
  5108. * True by default
  5109. * @sample {highcharts} highcharts/time/useutc-false/
  5110. * False
  5111. */
  5112. useUTC: true
  5113. },
  5114. chart: ChartDefaults,
  5115. /**
  5116. * The chart's main title.
  5117. *
  5118. * @sample {highmaps} maps/title/title/
  5119. * Title options demonstrated
  5120. */
  5121. title: {
  5122. /**
  5123. * When the title is floating, the plot area will not move to make space
  5124. * for it.
  5125. *
  5126. * @sample {highcharts} highcharts/chart/zoomtype-none/
  5127. * False by default
  5128. * @sample {highcharts} highcharts/title/floating/
  5129. * True - title on top of the plot area
  5130. * @sample {highstock} stock/chart/title-floating/
  5131. * True - title on top of the plot area
  5132. *
  5133. * @type {boolean}
  5134. * @default false
  5135. * @since 2.1
  5136. * @apioption title.floating
  5137. */
  5138. /**
  5139. * Whether to
  5140. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5141. * to render the text.
  5142. *
  5143. * @type {boolean}
  5144. * @default false
  5145. * @apioption title.useHTML
  5146. */
  5147. /**
  5148. * The vertical alignment of the title. Can be one of `"top"`,
  5149. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  5150. * as if [floating](#title.floating) were `true`.
  5151. *
  5152. * @sample {highcharts} highcharts/title/verticalalign/
  5153. * Chart title in bottom right corner
  5154. * @sample {highstock} stock/chart/title-verticalalign/
  5155. * Chart title in bottom right corner
  5156. *
  5157. * @type {Highcharts.VerticalAlignValue}
  5158. * @since 2.1
  5159. * @apioption title.verticalAlign
  5160. */
  5161. /**
  5162. * The x position of the title relative to the alignment within
  5163. * `chart.spacingLeft` and `chart.spacingRight`.
  5164. *
  5165. * @sample {highcharts} highcharts/title/align/
  5166. * Aligned to the plot area (x = 70px = margin left - spacing
  5167. * left)
  5168. * @sample {highstock} stock/chart/title-align/
  5169. * Aligned to the plot area (x = 50px = margin left - spacing
  5170. * left)
  5171. *
  5172. * @type {number}
  5173. * @default 0
  5174. * @since 2.0
  5175. * @apioption title.x
  5176. */
  5177. /**
  5178. * The y position of the title relative to the alignment within
  5179. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  5180. * #chart.spacingBottom). By default it depends on the font size.
  5181. *
  5182. * @sample {highcharts} highcharts/title/y/
  5183. * Title inside the plot area
  5184. * @sample {highstock} stock/chart/title-verticalalign/
  5185. * Chart title in bottom right corner
  5186. *
  5187. * @type {number}
  5188. * @since 2.0
  5189. * @apioption title.y
  5190. */
  5191. /**
  5192. * CSS styles for the title. Use this for font styling, but use `align`,
  5193. * `x` and `y` for text alignment.
  5194. *
  5195. * In styled mode, the title style is given in the `.highcharts-title`
  5196. * class.
  5197. *
  5198. * @sample {highcharts} highcharts/title/style/
  5199. * Custom color and weight
  5200. * @sample {highstock} stock/chart/title-style/
  5201. * Custom color and weight
  5202. * @sample highcharts/css/titles/
  5203. * Styled mode
  5204. *
  5205. * @type {Highcharts.CSSObject}
  5206. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  5207. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  5208. */
  5209. style: {
  5210. color: "#333333" /* Palette.neutralColor80 */,
  5211. fontWeight: 'bold'
  5212. },
  5213. /**
  5214. * The title of the chart. To disable the title, set the `text` to
  5215. * `undefined`.
  5216. *
  5217. * @sample {highcharts} highcharts/title/text/
  5218. * Custom title
  5219. * @sample {highstock} stock/chart/title-text/
  5220. * Custom title
  5221. *
  5222. * @default {highcharts|highmaps} Chart title
  5223. * @default {highstock} undefined
  5224. */
  5225. text: 'Chart title',
  5226. /**
  5227. * The horizontal alignment of the title. Can be one of "left", "center"
  5228. * and "right".
  5229. *
  5230. * @sample {highcharts} highcharts/title/align/
  5231. * Aligned to the plot area (x = 70px = margin left - spacing
  5232. * left)
  5233. * @sample {highstock} stock/chart/title-align/
  5234. * Aligned to the plot area (x = 50px = margin left - spacing
  5235. * left)
  5236. *
  5237. * @type {Highcharts.AlignValue}
  5238. * @since 2.0
  5239. */
  5240. align: 'center',
  5241. /**
  5242. * The margin between the title and the plot area, or if a subtitle
  5243. * is present, the margin between the subtitle and the plot area.
  5244. *
  5245. * @sample {highcharts} highcharts/title/margin-50/
  5246. * A chart title margin of 50
  5247. * @sample {highcharts} highcharts/title/margin-subtitle/
  5248. * The same margin applied with a subtitle
  5249. * @sample {highstock} stock/chart/title-margin/
  5250. * A chart title margin of 50
  5251. *
  5252. * @since 2.1
  5253. */
  5254. margin: 15,
  5255. /**
  5256. * Adjustment made to the title width, normally to reserve space for
  5257. * the exporting burger menu.
  5258. *
  5259. * @sample highcharts/title/widthadjust/
  5260. * Wider menu, greater padding
  5261. *
  5262. * @since 4.2.5
  5263. */
  5264. widthAdjust: -44
  5265. },
  5266. /**
  5267. * The chart's subtitle. This can be used both to display a subtitle below
  5268. * the main title, and to display random text anywhere in the chart. The
  5269. * subtitle can be updated after chart initialization through the
  5270. * `Chart.setTitle` method.
  5271. *
  5272. * @sample {highmaps} maps/title/subtitle/
  5273. * Subtitle options demonstrated
  5274. */
  5275. subtitle: {
  5276. /**
  5277. * When the subtitle is floating, the plot area will not move to make
  5278. * space for it.
  5279. *
  5280. * @sample {highcharts} highcharts/subtitle/floating/
  5281. * Floating title and subtitle
  5282. * @sample {highstock} stock/chart/subtitle-footnote
  5283. * Footnote floating at bottom right of plot area
  5284. *
  5285. * @type {boolean}
  5286. * @default false
  5287. * @since 2.1
  5288. * @apioption subtitle.floating
  5289. */
  5290. /**
  5291. * CSS styles for the title.
  5292. *
  5293. * In styled mode, the subtitle style is given in the
  5294. * `.highcharts-subtitle` class.
  5295. *
  5296. * @sample {highcharts} highcharts/subtitle/style/
  5297. * Custom color and weight
  5298. * @sample {highcharts} highcharts/css/titles/
  5299. * Styled mode
  5300. * @sample {highstock} stock/chart/subtitle-style
  5301. * Custom color and weight
  5302. * @sample {highstock} highcharts/css/titles/
  5303. * Styled mode
  5304. * @sample {highmaps} highcharts/css/titles/
  5305. * Styled mode
  5306. *
  5307. * @type {Highcharts.CSSObject}
  5308. * @default {"color": "#666666"}
  5309. * @apioption subtitle.style
  5310. */
  5311. /**
  5312. * Whether to
  5313. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5314. * to render the text.
  5315. *
  5316. * @type {boolean}
  5317. * @default false
  5318. * @apioption subtitle.useHTML
  5319. */
  5320. /**
  5321. * The vertical alignment of the title. Can be one of `"top"`,
  5322. * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
  5323. * floating.
  5324. *
  5325. * @sample {highcharts} highcharts/subtitle/verticalalign/
  5326. * Footnote at the bottom right of plot area
  5327. * @sample {highstock} stock/chart/subtitle-footnote
  5328. * Footnote at the bottom right of plot area
  5329. *
  5330. * @type {Highcharts.VerticalAlignValue}
  5331. * @since 2.1
  5332. * @apioption subtitle.verticalAlign
  5333. */
  5334. /**
  5335. * The x position of the subtitle relative to the alignment within
  5336. * `chart.spacingLeft` and `chart.spacingRight`.
  5337. *
  5338. * @sample {highcharts} highcharts/subtitle/align/
  5339. * Footnote at right of plot area
  5340. * @sample {highstock} stock/chart/subtitle-footnote
  5341. * Footnote at the bottom right of plot area
  5342. *
  5343. * @type {number}
  5344. * @default 0
  5345. * @since 2.0
  5346. * @apioption subtitle.x
  5347. */
  5348. /**
  5349. * The y position of the subtitle relative to the alignment within
  5350. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  5351. * is laid out below the title unless the title is floating.
  5352. *
  5353. * @sample {highcharts} highcharts/subtitle/verticalalign/
  5354. * Footnote at the bottom right of plot area
  5355. * @sample {highstock} stock/chart/subtitle-footnote
  5356. * Footnote at the bottom right of plot area
  5357. *
  5358. * @type {number}
  5359. * @since 2.0
  5360. * @apioption subtitle.y
  5361. */
  5362. /**
  5363. * CSS styles for the title.
  5364. *
  5365. * In styled mode, the subtitle style is given in the
  5366. * `.highcharts-subtitle` class.
  5367. *
  5368. * @sample {highcharts} highcharts/subtitle/style/
  5369. * Custom color and weight
  5370. * @sample {highcharts} highcharts/css/titles/
  5371. * Styled mode
  5372. * @sample {highstock} stock/chart/subtitle-style
  5373. * Custom color and weight
  5374. * @sample {highstock} highcharts/css/titles/
  5375. * Styled mode
  5376. * @sample {highmaps} highcharts/css/titles/
  5377. * Styled mode
  5378. *
  5379. * @type {Highcharts.CSSObject}
  5380. * @default {"color": "#666666"}
  5381. */
  5382. style: {
  5383. color: "#666666" /* Palette.neutralColor60 */,
  5384. fontSize: '0.8em'
  5385. },
  5386. /**
  5387. * The subtitle of the chart.
  5388. *
  5389. * @sample {highcharts|highstock} highcharts/subtitle/text/
  5390. * Custom subtitle
  5391. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  5392. * Formatted and linked text.
  5393. */
  5394. text: '',
  5395. /**
  5396. * The horizontal alignment of the subtitle. Can be one of "left",
  5397. * "center" and "right".
  5398. *
  5399. * @sample {highcharts} highcharts/subtitle/align/
  5400. * Footnote at right of plot area
  5401. * @sample {highstock} stock/chart/subtitle-footnote
  5402. * Footnote at bottom right of plot area
  5403. *
  5404. * @type {Highcharts.AlignValue}
  5405. * @since 2.0
  5406. */
  5407. align: 'center',
  5408. /**
  5409. * Adjustment made to the subtitle width, normally to reserve space
  5410. * for the exporting burger menu.
  5411. *
  5412. * @see [title.widthAdjust](#title.widthAdjust)
  5413. *
  5414. * @sample highcharts/title/widthadjust/
  5415. * Wider menu, greater padding
  5416. *
  5417. * @since 4.2.5
  5418. */
  5419. widthAdjust: -44
  5420. },
  5421. /**
  5422. * The chart's caption, which will render below the chart and will be part
  5423. * of exported charts. The caption can be updated after chart initialization
  5424. * through the `Chart.update` or `Chart.caption.update` methods.
  5425. *
  5426. * @sample highcharts/caption/text/
  5427. * A chart with a caption
  5428. * @since 7.2.0
  5429. */
  5430. caption: {
  5431. /**
  5432. * When the caption is floating, the plot area will not move to make
  5433. * space for it.
  5434. *
  5435. * @type {boolean}
  5436. * @default false
  5437. * @apioption caption.floating
  5438. */
  5439. /**
  5440. * The margin between the caption and the plot area.
  5441. */
  5442. margin: 15,
  5443. /**
  5444. * Whether to
  5445. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5446. * to render the text.
  5447. *
  5448. * @type {boolean}
  5449. * @default false
  5450. * @apioption caption.useHTML
  5451. */
  5452. /**
  5453. * The x position of the caption relative to the alignment within
  5454. * `chart.spacingLeft` and `chart.spacingRight`.
  5455. *
  5456. * @type {number}
  5457. * @default 0
  5458. * @apioption caption.x
  5459. */
  5460. /**
  5461. * The y position of the caption relative to the alignment within
  5462. * `chart.spacingTop` and `chart.spacingBottom`.
  5463. *
  5464. * @type {number}
  5465. * @apioption caption.y
  5466. */
  5467. /**
  5468. * CSS styles for the caption.
  5469. *
  5470. * In styled mode, the caption style is given in the
  5471. * `.highcharts-caption` class.
  5472. *
  5473. * @sample {highcharts} highcharts/css/titles/
  5474. * Styled mode
  5475. *
  5476. * @type {Highcharts.CSSObject}
  5477. * @default {"color": "#666666"}
  5478. */
  5479. style: {
  5480. color: "#666666" /* Palette.neutralColor60 */,
  5481. fontSize: '0.8em'
  5482. },
  5483. /**
  5484. * The caption text of the chart.
  5485. *
  5486. * @sample {highcharts} highcharts/caption/text/
  5487. * Custom caption
  5488. */
  5489. text: '',
  5490. /**
  5491. * The horizontal alignment of the caption. Can be one of "left",
  5492. * "center" and "right".
  5493. *
  5494. * @type {Highcharts.AlignValue}
  5495. */
  5496. align: 'left',
  5497. /**
  5498. * The vertical alignment of the caption. Can be one of `"top"`,
  5499. * `"middle"` and `"bottom"`. When middle, the caption behaves as
  5500. * floating.
  5501. *
  5502. * @type {Highcharts.VerticalAlignValue}
  5503. */
  5504. verticalAlign: 'bottom'
  5505. },
  5506. /**
  5507. * The plotOptions is a wrapper object for config objects for each series
  5508. * type. The config objects for each series can also be overridden for
  5509. * each series item as given in the series array.
  5510. *
  5511. * Configuration options for the series are given in three levels. Options
  5512. * for all series in a chart are given in the [plotOptions.series](
  5513. * #plotOptions.series) object. Then options for all series of a specific
  5514. * type are given in the plotOptions of that type, for example
  5515. * `plotOptions.line`. Next, options for one single series are given in
  5516. * [the series array](#series).
  5517. */
  5518. plotOptions: {},
  5519. /**
  5520. * The legend is a box containing a symbol and name for each series
  5521. * item or point item in the chart. Each series (or points in case
  5522. * of pie charts) is represented by a symbol and its name in the legend.
  5523. *
  5524. * It is possible to override the symbol creator function and create
  5525. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  5526. *
  5527. * @productdesc {highmaps}
  5528. * A Highmaps legend by default contains one legend item per series, but if
  5529. * a `colorAxis` is defined, the axis will be displayed in the legend.
  5530. * Either as a gradient, or as multiple legend items for `dataClasses`.
  5531. */
  5532. legend: {
  5533. /**
  5534. * The background color of the legend.
  5535. *
  5536. * @see In styled mode, the legend background fill can be applied with
  5537. * the `.highcharts-legend-box` class.
  5538. *
  5539. * @sample {highcharts} highcharts/legend/backgroundcolor/
  5540. * Yellowish background
  5541. * @sample {highstock} stock/legend/align/
  5542. * Various legend options
  5543. * @sample {highmaps} maps/legend/border-background/
  5544. * Border and background options
  5545. *
  5546. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5547. * @apioption legend.backgroundColor
  5548. */
  5549. /**
  5550. * The width of the drawn border around the legend.
  5551. *
  5552. * @see In styled mode, the legend border stroke width can be applied
  5553. * with the `.highcharts-legend-box` class.
  5554. *
  5555. * @sample {highcharts} highcharts/legend/borderwidth/
  5556. * 2px border width
  5557. * @sample {highstock} stock/legend/align/
  5558. * Various legend options
  5559. * @sample {highmaps} maps/legend/border-background/
  5560. * Border and background options
  5561. *
  5562. * @type {number}
  5563. * @default 0
  5564. * @apioption legend.borderWidth
  5565. */
  5566. /**
  5567. * Enable or disable the legend. There is also a series-specific option,
  5568. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  5569. * series from the legend. In some series types this is `false` by
  5570. * default, so it must set to `true` in order to show the legend for the
  5571. * series.
  5572. *
  5573. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  5574. * @sample {highstock} stock/legend/align/ Various legend options
  5575. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  5576. *
  5577. * @default {highstock} false
  5578. * @default {highmaps} true
  5579. * @default {gantt} false
  5580. */
  5581. enabled: true,
  5582. /**
  5583. * The horizontal alignment of the legend box within the chart area.
  5584. * Valid values are `left`, `center` and `right`.
  5585. *
  5586. * In the case that the legend is aligned in a corner position, the
  5587. * `layout` option will determine whether to place it above/below
  5588. * or on the side of the plot area.
  5589. *
  5590. * @sample {highcharts} highcharts/legend/align/
  5591. * Legend at the right of the chart
  5592. * @sample {highstock} stock/legend/align/
  5593. * Various legend options
  5594. * @sample {highmaps} maps/legend/alignment/
  5595. * Legend alignment
  5596. *
  5597. * @type {Highcharts.AlignValue}
  5598. * @since 2.0
  5599. */
  5600. align: 'center',
  5601. /**
  5602. * If the [layout](legend.layout) is `horizontal` and the legend items
  5603. * span over two lines or more, whether to align the items into vertical
  5604. * columns. Setting this to `false` makes room for more items, but will
  5605. * look more messy.
  5606. *
  5607. * @since 6.1.0
  5608. */
  5609. alignColumns: true,
  5610. /**
  5611. * A CSS class name to apply to the legend group.
  5612. */
  5613. className: 'highcharts-no-tooltip',
  5614. /**
  5615. * When the legend is floating, the plot area ignores it and is allowed
  5616. * to be placed below it.
  5617. *
  5618. * @sample {highcharts} highcharts/legend/floating-false/
  5619. * False by default
  5620. * @sample {highcharts} highcharts/legend/floating-true/
  5621. * True
  5622. * @sample {highmaps} maps/legend/alignment/
  5623. * Floating legend
  5624. *
  5625. * @type {boolean}
  5626. * @default false
  5627. * @since 2.1
  5628. * @apioption legend.floating
  5629. */
  5630. /**
  5631. * The layout of the legend items. Can be one of `horizontal` or
  5632. * `vertical` or `proximate`. When `proximate`, the legend items will be
  5633. * placed as close as possible to the graphs they're representing,
  5634. * except in inverted charts or when the legend position doesn't allow
  5635. * it.
  5636. *
  5637. * @sample {highcharts} highcharts/legend/layout-horizontal/
  5638. * Horizontal by default
  5639. * @sample {highcharts} highcharts/legend/layout-vertical/
  5640. * Vertical
  5641. * @sample highcharts/legend/layout-proximate
  5642. * Labels proximate to the data
  5643. * @sample {highstock} stock/legend/layout-horizontal/
  5644. * Horizontal by default
  5645. * @sample {highmaps} maps/legend/padding-itemmargin/
  5646. * Vertical with data classes
  5647. * @sample {highmaps} maps/legend/layout-vertical/
  5648. * Vertical with color axis gradient
  5649. *
  5650. * @validvalue ["horizontal", "vertical", "proximate"]
  5651. */
  5652. layout: 'horizontal',
  5653. /**
  5654. * In a legend with horizontal layout, the itemDistance defines the
  5655. * pixel distance between each item.
  5656. *
  5657. * @sample {highcharts} highcharts/legend/layout-horizontal/
  5658. * 50px item distance
  5659. * @sample {highstock} highcharts/legend/layout-horizontal/
  5660. * 50px item distance
  5661. *
  5662. * @type {number}
  5663. * @default {highcharts} 20
  5664. * @default {highstock} 20
  5665. * @default {highmaps} 8
  5666. * @since 3.0.3
  5667. * @apioption legend.itemDistance
  5668. */
  5669. /**
  5670. * The pixel bottom margin for each legend item.
  5671. *
  5672. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5673. * Padding and item margins demonstrated
  5674. * @sample {highmaps} maps/legend/padding-itemmargin/
  5675. * Padding and item margins demonstrated
  5676. *
  5677. * @since 2.2.0
  5678. */
  5679. itemMarginBottom: 2,
  5680. /**
  5681. * The pixel top margin for each legend item.
  5682. *
  5683. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5684. * Padding and item margins demonstrated
  5685. * @sample {highmaps} maps/legend/padding-itemmargin/
  5686. * Padding and item margins demonstrated
  5687. *
  5688. * @since 2.2.0
  5689. */
  5690. itemMarginTop: 2,
  5691. /**
  5692. * The width for each legend item. By default the items are laid out
  5693. * successively. In a [horizontal layout](legend.layout), if the items
  5694. * are laid out across two rows or more, they will be vertically aligned
  5695. * depending on the [legend.alignColumns](legend.alignColumns) option.
  5696. *
  5697. * @sample {highcharts} highcharts/legend/itemwidth-default/
  5698. * Undefined by default
  5699. * @sample {highcharts} highcharts/legend/itemwidth-80/
  5700. * 80 for aligned legend items
  5701. *
  5702. * @type {number}
  5703. * @since 2.0
  5704. * @apioption legend.itemWidth
  5705. */
  5706. /**
  5707. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  5708. * for each legend label. Available variables relates to properties on
  5709. * the series, or the point in case of pies.
  5710. *
  5711. * @type {string}
  5712. * @default {name}
  5713. * @since 1.3
  5714. * @apioption legend.labelFormat
  5715. */
  5716. /* eslint-disable valid-jsdoc */
  5717. /**
  5718. * Callback function to format each of the series' labels. The `this`
  5719. * keyword refers to the series object, or the point object in case of
  5720. * pie charts. By default the series or point name is printed.
  5721. *
  5722. * @productdesc {highmaps}
  5723. * In Highmaps the context can also be a data class in case of a
  5724. * `colorAxis`.
  5725. *
  5726. * @sample {highcharts} highcharts/legend/labelformatter/
  5727. * Add text
  5728. * @sample {highmaps} maps/legend/labelformatter/
  5729. * Data classes with label formatter
  5730. *
  5731. * @type {Highcharts.FormatterCallbackFunction<Point|Series>}
  5732. */
  5733. labelFormatter: function () {
  5734. /** eslint-enable valid-jsdoc */
  5735. return this.name;
  5736. },
  5737. /**
  5738. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  5739. * the line height for each item can be set using
  5740. * `itemStyle.lineHeight`, and the padding between items using
  5741. * `itemMarginTop` and `itemMarginBottom`.
  5742. *
  5743. * @sample {highcharts} highcharts/legend/lineheight/
  5744. * Setting padding
  5745. *
  5746. * @deprecated
  5747. *
  5748. * @type {number}
  5749. * @default 16
  5750. * @since 2.0
  5751. * @product highcharts gantt
  5752. * @apioption legend.lineHeight
  5753. */
  5754. /**
  5755. * If the plot area sized is calculated automatically and the legend is
  5756. * not floating, the legend margin is the space between the legend and
  5757. * the axis labels or plot area.
  5758. *
  5759. * @sample {highcharts} highcharts/legend/margin-default/
  5760. * 12 pixels by default
  5761. * @sample {highcharts} highcharts/legend/margin-30/
  5762. * 30 pixels
  5763. *
  5764. * @type {number}
  5765. * @default 12
  5766. * @since 2.1
  5767. * @apioption legend.margin
  5768. */
  5769. /**
  5770. * Maximum pixel height for the legend. When the maximum height is
  5771. * extended, navigation will show.
  5772. *
  5773. * @type {number}
  5774. * @since 2.3.0
  5775. * @apioption legend.maxHeight
  5776. */
  5777. /**
  5778. * The color of the drawn border around the legend.
  5779. *
  5780. * @see In styled mode, the legend border stroke can be applied with the
  5781. * `.highcharts-legend-box` class.
  5782. *
  5783. * @sample {highcharts} highcharts/legend/bordercolor/
  5784. * Brown border
  5785. * @sample {highstock} stock/legend/align/
  5786. * Various legend options
  5787. * @sample {highmaps} maps/legend/border-background/
  5788. * Border and background options
  5789. *
  5790. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5791. */
  5792. borderColor: "#999999" /* Palette.neutralColor40 */,
  5793. /**
  5794. * The border corner radius of the legend.
  5795. *
  5796. * @sample {highcharts} highcharts/legend/borderradius-default/
  5797. * Square by default
  5798. * @sample {highcharts} highcharts/legend/borderradius-round/
  5799. * 5px rounded
  5800. * @sample {highmaps} maps/legend/border-background/
  5801. * Border and background options
  5802. */
  5803. borderRadius: 0,
  5804. /**
  5805. * Options for the paging or navigation appearing when the legend is
  5806. * overflown. Navigation works well on screen, but not in static
  5807. * exported images. One way of working around that is to
  5808. * [increase the chart height in
  5809. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  5810. */
  5811. navigation: {
  5812. /**
  5813. * How to animate the pages when navigating up or down. A value of
  5814. * `true` applies the default navigation given in the
  5815. * `chart.animation` option. Additional options can be given as an
  5816. * object containing values for easing and duration.
  5817. *
  5818. * @sample {highcharts} highcharts/legend/navigation/
  5819. * Legend page navigation demonstrated
  5820. * @sample {highstock} highcharts/legend/navigation/
  5821. * Legend page navigation demonstrated
  5822. *
  5823. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  5824. * @default true
  5825. * @since 2.2.4
  5826. * @apioption legend.navigation.animation
  5827. */
  5828. /**
  5829. * The pixel size of the up and down arrows in the legend paging
  5830. * navigation.
  5831. *
  5832. * @sample {highcharts} highcharts/legend/navigation/
  5833. * Legend page navigation demonstrated
  5834. * @sample {highstock} highcharts/legend/navigation/
  5835. * Legend page navigation demonstrated
  5836. *
  5837. * @type {number}
  5838. * @default 12
  5839. * @since 2.2.4
  5840. * @apioption legend.navigation.arrowSize
  5841. */
  5842. /**
  5843. * Whether to enable the legend navigation. In most cases, disabling
  5844. * the navigation results in an unwanted overflow.
  5845. *
  5846. * See also the
  5847. * [adapt chart to legend](https://github.com/highcharts/adapt-chart-to-legend)
  5848. * plugin for a solution to extend the chart height to make room for
  5849. * the legend, optionally in exported charts only.
  5850. *
  5851. * @type {boolean}
  5852. * @default true
  5853. * @since 4.2.4
  5854. * @apioption legend.navigation.enabled
  5855. */
  5856. /**
  5857. * Text styles for the legend page navigation.
  5858. *
  5859. * @see In styled mode, the navigation items are styled with the
  5860. * `.highcharts-legend-navigation` class.
  5861. *
  5862. * @sample {highcharts} highcharts/legend/navigation/
  5863. * Legend page navigation demonstrated
  5864. * @sample {highstock} highcharts/legend/navigation/
  5865. * Legend page navigation demonstrated
  5866. *
  5867. * @type {Highcharts.CSSObject}
  5868. * @since 2.2.4
  5869. * @apioption legend.navigation.style
  5870. */
  5871. style: {
  5872. fontSize: '0.8em'
  5873. },
  5874. /**
  5875. * The color for the active up or down arrow in the legend page
  5876. * navigation.
  5877. *
  5878. * @see In styled mode, the active arrow be styled with the
  5879. * `.highcharts-legend-nav-active` class.
  5880. *
  5881. * @sample {highcharts} highcharts/legend/navigation/
  5882. * Legend page navigation demonstrated
  5883. * @sample {highstock} highcharts/legend/navigation/
  5884. * Legend page navigation demonstrated
  5885. *
  5886. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5887. * @since 2.2.4
  5888. */
  5889. activeColor: "#0022ff" /* Palette.highlightColor100 */,
  5890. /**
  5891. * The color of the inactive up or down arrow in the legend page
  5892. * navigation. .
  5893. *
  5894. * @see In styled mode, the inactive arrow be styled with the
  5895. * `.highcharts-legend-nav-inactive` class.
  5896. *
  5897. * @sample {highcharts} highcharts/legend/navigation/
  5898. * Legend page navigation demonstrated
  5899. * @sample {highstock} highcharts/legend/navigation/
  5900. * Legend page navigation demonstrated
  5901. *
  5902. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5903. * @since 2.2.4
  5904. */
  5905. inactiveColor: "#cccccc" /* Palette.neutralColor20 */
  5906. },
  5907. /**
  5908. * The inner padding of the legend box.
  5909. *
  5910. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5911. * Padding and item margins demonstrated
  5912. * @sample {highmaps} maps/legend/padding-itemmargin/
  5913. * Padding and item margins demonstrated
  5914. *
  5915. * @type {number}
  5916. * @default 8
  5917. * @since 2.2.0
  5918. * @apioption legend.padding
  5919. */
  5920. /**
  5921. * Whether to reverse the order of the legend items compared to the
  5922. * order of the series or points as defined in the configuration object.
  5923. *
  5924. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  5925. * [series.legendIndex](#series.legendIndex)
  5926. *
  5927. * @sample {highcharts} highcharts/legend/reversed/
  5928. * Stacked bar with reversed legend
  5929. *
  5930. * @type {boolean}
  5931. * @default false
  5932. * @since 1.2.5
  5933. * @apioption legend.reversed
  5934. */
  5935. /**
  5936. * Whether to show the symbol on the right side of the text rather than
  5937. * the left side. This is common in Arabic and Hebrew.
  5938. *
  5939. * @sample {highcharts} highcharts/legend/rtl/
  5940. * Symbol to the right
  5941. *
  5942. * @type {boolean}
  5943. * @default false
  5944. * @since 2.2
  5945. * @apioption legend.rtl
  5946. */
  5947. /**
  5948. * CSS styles for the legend area. In the 1.x versions the position
  5949. * of the legend area was determined by CSS. In 2.x, the position is
  5950. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  5951. * but the styles are still parsed for backwards compatibility.
  5952. *
  5953. * @deprecated
  5954. *
  5955. * @type {Highcharts.CSSObject}
  5956. * @product highcharts highstock
  5957. * @apioption legend.style
  5958. */
  5959. /**
  5960. * CSS styles for each legend item. Only a subset of CSS is supported,
  5961. * notably those options related to text. The default `textOverflow`
  5962. * property makes long texts truncate. Set it to `undefined` to wrap
  5963. * text instead. A `width` property can be added to control the text
  5964. * width.
  5965. *
  5966. * @see In styled mode, the legend items can be styled with the
  5967. * `.highcharts-legend-item` class.
  5968. *
  5969. * @sample {highcharts} highcharts/legend/itemstyle/
  5970. * Bold black text
  5971. * @sample {highmaps} maps/legend/itemstyle/
  5972. * Item text styles
  5973. *
  5974. * @type {Highcharts.CSSObject}
  5975. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "0.75em", "fontWeight": "bold", "textOverflow": "ellipsis"}
  5976. */
  5977. itemStyle: {
  5978. /**
  5979. * @ignore
  5980. */
  5981. color: "#333333" /* Palette.neutralColor80 */,
  5982. /**
  5983. * @ignore
  5984. */
  5985. cursor: 'pointer',
  5986. /**
  5987. * @ignore
  5988. */
  5989. fontSize: '0.8em',
  5990. /**
  5991. * @ignore
  5992. */
  5993. textDecoration: 'none',
  5994. /**
  5995. * @ignore
  5996. */
  5997. textOverflow: 'ellipsis'
  5998. },
  5999. /**
  6000. * CSS styles for each legend item in hover mode. Only a subset of
  6001. * CSS is supported, notably those options related to text. Properties
  6002. * are inherited from `style` unless overridden here.
  6003. *
  6004. * @see In styled mode, the hovered legend items can be styled with
  6005. * the `.highcharts-legend-item:hover` pesudo-class.
  6006. *
  6007. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  6008. * Red on hover
  6009. * @sample {highmaps} maps/legend/itemstyle/
  6010. * Item text styles
  6011. *
  6012. * @type {Highcharts.CSSObject}
  6013. * @default {"color": "#000000"}
  6014. */
  6015. itemHoverStyle: {
  6016. /**
  6017. * @ignore
  6018. */
  6019. color: "#000000" /* Palette.neutralColor100 */
  6020. },
  6021. /**
  6022. * CSS styles for each legend item when the corresponding series or
  6023. * point is hidden. Only a subset of CSS is supported, notably those
  6024. * options related to text. Properties are inherited from `style`
  6025. * unless overridden here.
  6026. *
  6027. * @see In styled mode, the hidden legend items can be styled with
  6028. * the `.highcharts-legend-item-hidden` class.
  6029. *
  6030. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  6031. * Darker gray color
  6032. *
  6033. * @type {Highcharts.CSSObject}
  6034. * @default {"color": "#cccccc"}
  6035. */
  6036. itemHiddenStyle: {
  6037. /**
  6038. * @ignore
  6039. */
  6040. color: "#666666" /* Palette.neutralColor60 */,
  6041. /**
  6042. * @ignore
  6043. */
  6044. textDecoration: 'line-through'
  6045. },
  6046. /**
  6047. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  6048. * also needs to be applied for this to take effect. The shadow can be
  6049. * an object configuration containing `color`, `offsetX`, `offsetY`,
  6050. * `opacity` and `width`.
  6051. *
  6052. * @sample {highcharts} highcharts/legend/shadow/
  6053. * White background and drop shadow
  6054. * @sample {highstock} stock/legend/align/
  6055. * Various legend options
  6056. * @sample {highmaps} maps/legend/border-background/
  6057. * Border and background options
  6058. *
  6059. * @type {boolean|Highcharts.CSSObject}
  6060. */
  6061. shadow: false,
  6062. /**
  6063. * Default styling for the checkbox next to a legend item when
  6064. * `showCheckbox` is true.
  6065. *
  6066. * @type {Highcharts.CSSObject}
  6067. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  6068. */
  6069. itemCheckboxStyle: {
  6070. /**
  6071. * @ignore
  6072. */
  6073. position: 'absolute',
  6074. /**
  6075. * @ignore
  6076. */
  6077. width: '13px',
  6078. /**
  6079. * @ignore
  6080. */
  6081. height: '13px'
  6082. },
  6083. // itemWidth: undefined,
  6084. /**
  6085. * When this is true, the legend symbol width will be the same as
  6086. * the symbol height, which in turn defaults to the font size of the
  6087. * legend items.
  6088. *
  6089. * @since 5.0.0
  6090. */
  6091. squareSymbol: true,
  6092. /**
  6093. * The pixel height of the symbol for series types that use a rectangle
  6094. * in the legend. Defaults to the font size of legend items.
  6095. *
  6096. * @productdesc {highmaps}
  6097. * In Highmaps, when the symbol is the gradient of a vertical color
  6098. * axis, the height defaults to 200.
  6099. *
  6100. * @sample {highmaps} maps/legend/layout-vertical-sized/
  6101. * Sized vertical gradient
  6102. * @sample {highmaps} maps/legend/padding-itemmargin/
  6103. * No distance between data classes
  6104. *
  6105. * @type {number}
  6106. * @since 3.0.8
  6107. * @apioption legend.symbolHeight
  6108. */
  6109. /**
  6110. * The border radius of the symbol for series types that use a rectangle
  6111. * in the legend. Defaults to half the `symbolHeight`, effectively
  6112. * creating a circle.
  6113. *
  6114. * For color axis scales, it defaults to 3.
  6115. *
  6116. * @sample {highcharts} highcharts/legend/symbolradius/
  6117. * Round symbols
  6118. * @sample {highstock} highcharts/legend/symbolradius/
  6119. * Round symbols
  6120. * @sample {highmaps} highcharts/legend/symbolradius/
  6121. * Round symbols
  6122. *
  6123. * @type {number}
  6124. * @since 3.0.8
  6125. * @apioption legend.symbolRadius
  6126. */
  6127. /**
  6128. * The pixel width of the legend item symbol. When the `squareSymbol`
  6129. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  6130. *
  6131. * @productdesc {highmaps}
  6132. * In Highmaps, when the symbol is the gradient of a horizontal color
  6133. * axis, the width defaults to 200.
  6134. *
  6135. * @sample {highcharts} highcharts/legend/symbolwidth/
  6136. * Greater symbol width and padding
  6137. * @sample {highmaps} maps/legend/padding-itemmargin/
  6138. * Padding and item margins demonstrated
  6139. * @sample {highmaps} maps/legend/layout-vertical-sized/
  6140. * Sized vertical gradient
  6141. *
  6142. * @type {number}
  6143. * @apioption legend.symbolWidth
  6144. */
  6145. /**
  6146. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  6147. * to render the legend item texts.
  6148. *
  6149. * Prior to 4.1.7, when using HTML, [legend.navigation](
  6150. * #legend.navigation) was disabled.
  6151. *
  6152. * @type {boolean}
  6153. * @default false
  6154. * @apioption legend.useHTML
  6155. */
  6156. /**
  6157. * For a color axis with data classes, how many decimals to render in
  6158. * the legend. The default preserves the decimals of the range numbers.
  6159. *
  6160. * @type {number}
  6161. * @default -1
  6162. * @product highcharts highmaps
  6163. * @apioption legend.valueDecimals
  6164. */
  6165. /**
  6166. * For a color axis with data classes, a suffix for the range numbers in
  6167. * the legend.
  6168. *
  6169. * @type {string}
  6170. * @default ''
  6171. * @product highcharts highmaps
  6172. * @apioption legend.valueSuffix
  6173. */
  6174. /**
  6175. * The width of the legend box. If a number is set, it translates to
  6176. * pixels. Since v7.0.2 it allows setting a percent string of the full
  6177. * chart width, for example `40%`.
  6178. *
  6179. * Defaults to the full chart width for legends below or above the
  6180. * chart, half the chart width for legends to the left and right.
  6181. *
  6182. * @sample {highcharts} highcharts/legend/width/
  6183. * Aligned to the plot area
  6184. * @sample {highcharts} highcharts/legend/width-percent/
  6185. * A percent of the chart width
  6186. *
  6187. * @type {number|string}
  6188. * @since 2.0
  6189. * @apioption legend.width
  6190. */
  6191. /**
  6192. * The pixel padding between the legend item symbol and the legend
  6193. * item text.
  6194. *
  6195. * @sample {highcharts} highcharts/legend/symbolpadding/
  6196. * Greater symbol width and padding
  6197. */
  6198. symbolPadding: 5,
  6199. /**
  6200. * The vertical alignment of the legend box. Can be one of `top`,
  6201. * `middle` or `bottom`. Vertical position can be further determined
  6202. * by the `y` option.
  6203. *
  6204. * In the case that the legend is aligned in a corner position, the
  6205. * `layout` option will determine whether to place it above/below
  6206. * or on the side of the plot area.
  6207. *
  6208. * When the [layout](#legend.layout) option is `proximate`, the
  6209. * `verticalAlign` option doesn't apply.
  6210. *
  6211. * @sample {highcharts} highcharts/legend/verticalalign/
  6212. * Legend 100px from the top of the chart
  6213. * @sample {highstock} stock/legend/align/
  6214. * Various legend options
  6215. * @sample {highmaps} maps/legend/alignment/
  6216. * Legend alignment
  6217. *
  6218. * @type {Highcharts.VerticalAlignValue}
  6219. * @since 2.0
  6220. */
  6221. verticalAlign: 'bottom',
  6222. // width: undefined,
  6223. /**
  6224. * The x offset of the legend relative to its horizontal alignment
  6225. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  6226. * x moves it to the left, positive x moves it to the right.
  6227. *
  6228. * @sample {highcharts} highcharts/legend/width/
  6229. * Aligned to the plot area
  6230. *
  6231. * @since 2.0
  6232. */
  6233. x: 0,
  6234. /**
  6235. * The vertical offset of the legend relative to it's vertical alignment
  6236. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  6237. * Negative y moves it up, positive y moves it down.
  6238. *
  6239. * @sample {highcharts} highcharts/legend/verticalalign/
  6240. * Legend 100px from the top of the chart
  6241. * @sample {highstock} stock/legend/align/
  6242. * Various legend options
  6243. * @sample {highmaps} maps/legend/alignment/
  6244. * Legend alignment
  6245. *
  6246. * @since 2.0
  6247. */
  6248. y: 0,
  6249. /**
  6250. * A title to be added on top of the legend.
  6251. *
  6252. * @sample {highcharts} highcharts/legend/title/
  6253. * Legend title
  6254. * @sample {highmaps} maps/legend/alignment/
  6255. * Legend with title
  6256. *
  6257. * @since 3.0
  6258. */
  6259. title: {
  6260. /**
  6261. * A text or HTML string for the title.
  6262. *
  6263. * @type {string}
  6264. * @since 3.0
  6265. * @apioption legend.title.text
  6266. */
  6267. /**
  6268. * Generic CSS styles for the legend title.
  6269. *
  6270. * @see In styled mode, the legend title is styled with the
  6271. * `.highcharts-legend-title` class.
  6272. *
  6273. * @type {Highcharts.CSSObject}
  6274. * @default {"fontSize": "0.75em", "fontWeight": "bold"}
  6275. * @since 3.0
  6276. */
  6277. style: {
  6278. /**
  6279. * @ignore
  6280. */
  6281. fontSize: '0.8em',
  6282. /**
  6283. * @ignore
  6284. */
  6285. fontWeight: 'bold'
  6286. }
  6287. }
  6288. },
  6289. /**
  6290. * The loading options control the appearance of the loading screen
  6291. * that covers the plot area on chart operations. This screen only
  6292. * appears after an explicit call to `chart.showLoading()`. It is a
  6293. * utility for developers to communicate to the end user that something
  6294. * is going on, for example while retrieving new data via an XHR connection.
  6295. * The "Loading..." text itself is not part of this configuration
  6296. * object, but part of the `lang` object.
  6297. */
  6298. loading: {
  6299. /**
  6300. * The duration in milliseconds of the fade out effect.
  6301. *
  6302. * @sample highcharts/loading/hideduration/
  6303. * Fade in and out over a second
  6304. *
  6305. * @type {number}
  6306. * @default 100
  6307. * @since 1.2.0
  6308. * @apioption loading.hideDuration
  6309. */
  6310. /**
  6311. * The duration in milliseconds of the fade in effect.
  6312. *
  6313. * @sample highcharts/loading/hideduration/
  6314. * Fade in and out over a second
  6315. *
  6316. * @type {number}
  6317. * @default 100
  6318. * @since 1.2.0
  6319. * @apioption loading.showDuration
  6320. */
  6321. /**
  6322. * CSS styles for the loading label `span`.
  6323. *
  6324. * @see In styled mode, the loading label is styled with the
  6325. * `.highcharts-loading-inner` class.
  6326. *
  6327. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  6328. * Vertically centered
  6329. * @sample {highstock} stock/loading/general/
  6330. * Label styles
  6331. *
  6332. * @type {Highcharts.CSSObject}
  6333. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  6334. * @since 1.2.0
  6335. */
  6336. labelStyle: {
  6337. /**
  6338. * @ignore
  6339. */
  6340. fontWeight: 'bold',
  6341. /**
  6342. * @ignore
  6343. */
  6344. position: 'relative',
  6345. /**
  6346. * @ignore
  6347. */
  6348. top: '45%'
  6349. },
  6350. /**
  6351. * CSS styles for the loading screen that covers the plot area.
  6352. *
  6353. * In styled mode, the loading label is styled with the
  6354. * `.highcharts-loading` class.
  6355. *
  6356. * @sample {highcharts|highmaps} highcharts/loading/style/
  6357. * Gray plot area, white text
  6358. * @sample {highstock} stock/loading/general/
  6359. * Gray plot area, white text
  6360. *
  6361. * @type {Highcharts.CSSObject}
  6362. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  6363. * @since 1.2.0
  6364. */
  6365. style: {
  6366. /**
  6367. * @ignore
  6368. */
  6369. position: 'absolute',
  6370. /**
  6371. * @ignore
  6372. */
  6373. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  6374. /**
  6375. * @ignore
  6376. */
  6377. opacity: 0.5,
  6378. /**
  6379. * @ignore
  6380. */
  6381. textAlign: 'center'
  6382. }
  6383. },
  6384. /**
  6385. * Options for the tooltip that appears when the user hovers over a
  6386. * series or point.
  6387. *
  6388. * @declare Highcharts.TooltipOptions
  6389. */
  6390. tooltip: {
  6391. /**
  6392. * The color of the tooltip border. When `undefined`, the border takes
  6393. * the color of the corresponding series or point.
  6394. *
  6395. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6396. * Follow series by default
  6397. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  6398. * Black border
  6399. * @sample {highstock} stock/tooltip/general/
  6400. * Styled tooltip
  6401. * @sample {highmaps} maps/tooltip/background-border/
  6402. * Background and border demo
  6403. *
  6404. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6405. * @apioption tooltip.borderColor
  6406. */
  6407. /**
  6408. * A CSS class name to apply to the tooltip's container div,
  6409. * allowing unique CSS styling for each chart.
  6410. *
  6411. * @type {string}
  6412. * @apioption tooltip.className
  6413. */
  6414. /**
  6415. * Since 4.1, the crosshair definitions are moved to the Axis object
  6416. * in order for a better separation from the tooltip. See
  6417. * [xAxis.crosshair](#xAxis.crosshair).
  6418. *
  6419. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  6420. * Enable a crosshair for the x value
  6421. *
  6422. * @deprecated
  6423. *
  6424. * @type {*}
  6425. * @default true
  6426. * @apioption tooltip.crosshairs
  6427. */
  6428. /**
  6429. * Distance from point to tooltip in pixels.
  6430. *
  6431. * @type {number}
  6432. * @default 16
  6433. * @apioption tooltip.distance
  6434. */
  6435. /**
  6436. * Whether the tooltip should follow the mouse as it moves across
  6437. * columns, pie slices and other point types with an extent.
  6438. * By default it behaves this way for pie, polygon, map, sankey
  6439. * and wordcloud series by override in the `plotOptions`
  6440. * for those series types.
  6441. *
  6442. * Does not apply if [split](#tooltip.split) is `true`.
  6443. *
  6444. * For touch moves to behave the same way, [followTouchMove](
  6445. * #tooltip.followTouchMove) must be `true` also.
  6446. *
  6447. * @sample highcharts/tooltip/followpointer/
  6448. * Tooltip follow pointer comparison
  6449. *
  6450. * @type {boolean}
  6451. * @default {highcharts} false
  6452. * @default {highstock} false
  6453. * @default {highmaps} true
  6454. * @since 3.0
  6455. * @apioption tooltip.followPointer
  6456. */
  6457. /**
  6458. * Whether the tooltip should update as the finger moves on a touch
  6459. * device. If this is `true` and [chart.panning](#chart.panning) is
  6460. * set,`followTouchMove` will take over one-finger touches, so the user
  6461. * needs to use two fingers for zooming and panning.
  6462. *
  6463. * Note the difference to [followPointer](#tooltip.followPointer) that
  6464. * only defines the _position_ of the tooltip. If `followPointer` is
  6465. * false in for example a column series, the tooltip will show above or
  6466. * below the column, but as `followTouchMove` is true, the tooltip will
  6467. * jump from column to column as the user swipes across the plot area.
  6468. *
  6469. * @type {boolean}
  6470. * @default {highcharts} true
  6471. * @default {highstock} true
  6472. * @default {highmaps} false
  6473. * @since 3.0.1
  6474. * @apioption tooltip.followTouchMove
  6475. */
  6476. /**
  6477. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  6478. * for the whole tooltip. When format strings are a requirement, it is
  6479. * usually more convenient to use `headerFormat`, `pointFormat` and
  6480. * `footerFormat`, but the `format` option allows combining them into
  6481. * one setting.
  6482. *
  6483. * The context of the format string is the same as that of the
  6484. * `formatter` callback.
  6485. *
  6486. * @sample {highcharts} highcharts/tooltip/format-shared/
  6487. * Format for shared tooltip
  6488. *
  6489. * @type {string}
  6490. * @default undefined
  6491. * @since 11.1.0
  6492. * @apioption tooltip.format
  6493. */
  6494. /**
  6495. * Callback function to format the text of the tooltip from scratch. In
  6496. * case of single or [shared](#tooltip.shared) tooltips, a string should
  6497. * be returned. In case of [split](#tooltip.split) tooltips, it should
  6498. * return an array where the first item is the header, and subsequent
  6499. * items are mapped to the points. Return `false` to disable tooltip for
  6500. * a specific point on series.
  6501. *
  6502. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  6503. * the tooltip is parsed and converted to SVG, therefore this isn't a
  6504. * complete HTML renderer. The following HTML tags are supported: `b`,
  6505. * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
  6506. * attribute, but only text-related CSS, that is shared with SVG, is
  6507. * handled.
  6508. *
  6509. * The available data in the formatter differ a bit depending on whether
  6510. * the tooltip is shared or split, or belongs to a single point. In a
  6511. * shared/split tooltip, all properties except `x`, which is common for
  6512. * all points, are kept in an array, `this.points`.
  6513. *
  6514. * Available data are:
  6515. *
  6516. * - **this.percentage (not shared) /**
  6517. * **this.points[i].percentage (shared)**:
  6518. * Stacked series and pies only. The point's percentage of the total.
  6519. *
  6520. * - **this.point (not shared) / this.points[i].point (shared)**:
  6521. * The point object. The point name, if defined, is available through
  6522. * `this.point.name`.
  6523. *
  6524. * - **this.points**:
  6525. * In a shared tooltip, this is an array containing all other
  6526. * properties for each point.
  6527. *
  6528. * - **this.series (not shared) / this.points[i].series (shared)**:
  6529. * The series object. The series name is available through
  6530. * `this.series.name`.
  6531. *
  6532. * - **this.total (not shared) / this.points[i].total (shared)**:
  6533. * Stacked series only. The total value at this point's x value.
  6534. *
  6535. * - **this.x**:
  6536. * The x value. This property is the same regardless of the tooltip
  6537. * being shared or not.
  6538. *
  6539. * - **this.y (not shared) / this.points[i].y (shared)**:
  6540. * The y value.
  6541. *
  6542. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  6543. * Simple string formatting
  6544. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  6545. * Formatting with shared tooltip
  6546. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  6547. * Formatting with split tooltip
  6548. * @sample highcharts/tooltip/formatter-conditional-default/
  6549. * Extending default formatter
  6550. * @sample {highstock} stock/tooltip/formatter/
  6551. * Formatting with shared tooltip
  6552. * @sample {highmaps} maps/tooltip/formatter/
  6553. * String formatting
  6554. *
  6555. * @type {Highcharts.TooltipFormatterCallbackFunction}
  6556. * @apioption tooltip.formatter
  6557. */
  6558. /**
  6559. * Callback function to format the text of the tooltip for
  6560. * visible null points.
  6561. * Works analogously to [formatter](#tooltip.formatter).
  6562. *
  6563. * @sample highcharts/plotoptions/series-nullformat
  6564. * Format data label and tooltip for null point.
  6565. *
  6566. * @type {Highcharts.TooltipFormatterCallbackFunction}
  6567. * @apioption tooltip.nullFormatter
  6568. */
  6569. /**
  6570. * Whether to allow the tooltip to render outside the chart's SVG
  6571. * element box. By default (`false`), the tooltip is rendered within the
  6572. * chart's SVG element, which results in the tooltip being aligned
  6573. * inside the chart area. For small charts, this may result in clipping
  6574. * or overlapping. When `true`, a separate SVG element is created and
  6575. * overlaid on the page, allowing the tooltip to be aligned inside the
  6576. * page itself.
  6577. *
  6578. * Defaults to `true` if `chart.scrollablePlotArea` is activated,
  6579. * otherwise `false`.
  6580. *
  6581. * @sample highcharts/tooltip/outside
  6582. * Small charts with tooltips outside
  6583. *
  6584. * @type {boolean|undefined}
  6585. * @default undefined
  6586. * @since 6.1.1
  6587. * @apioption tooltip.outside
  6588. */
  6589. /**
  6590. * A callback function for formatting the HTML output for a single point
  6591. * in the tooltip. Like the `pointFormat` string, but with more
  6592. * flexibility.
  6593. *
  6594. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  6595. * @since 4.1.0
  6596. * @context Highcharts.Point
  6597. * @apioption tooltip.pointFormatter
  6598. */
  6599. /**
  6600. * A callback function to place the tooltip in a custom position. The
  6601. * callback receives three parameters: `labelWidth`, `labelHeight` and
  6602. * `point`, where point contains values for `plotX` and `plotY` telling
  6603. * where the reference point is in the plot area. Add `chart.plotLeft`
  6604. * and `chart.plotTop` to get the full coordinates.
  6605. *
  6606. * To find the actual hovered `Point` instance, use
  6607. * `this.chart.hoverPoint`. For shared or split tooltips, all the hover
  6608. * points are available in `this.chart.hoverPoints`.
  6609. *
  6610. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  6611. * positioner is called for each of the boxes separately, including
  6612. * xAxis header. xAxis header is not a point, instead `point` argument
  6613. * contains info: `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  6614. *
  6615. * The return should be an object containing x and y values, for example
  6616. * `{ x: 100, y: 100 }`.
  6617. *
  6618. * @sample {highcharts} highcharts/tooltip/positioner/
  6619. * A fixed tooltip position
  6620. * @sample {highstock} stock/tooltip/positioner/
  6621. * A fixed tooltip position on top of the chart
  6622. * @sample {highmaps} maps/tooltip/positioner/
  6623. * A fixed tooltip position
  6624. * @sample {highstock} stock/tooltip/split-positioner/
  6625. * Split tooltip with fixed positions
  6626. * @sample {highstock} stock/tooltip/positioner-scrollable-plotarea/
  6627. * Scrollable plot area combined with tooltip positioner
  6628. *
  6629. * @type {Highcharts.TooltipPositionerCallbackFunction}
  6630. * @since 2.2.4
  6631. * @apioption tooltip.positioner
  6632. */
  6633. /**
  6634. * Split the tooltip into one label per series, with the header close
  6635. * to the axis. This is recommended over [shared](#tooltip.shared)
  6636. * tooltips for charts with multiple line series, generally making them
  6637. * easier to read. This option takes precedence over `tooltip.shared`.
  6638. *
  6639. * Not supported for [polar](#chart.polar) and [inverted](#chart.inverted) charts.
  6640. *
  6641. * @productdesc {highstock} In Highcharts Stock, tooltips are split
  6642. * by default since v6.0.0. Stock charts typically contain
  6643. * multi-dimension points and multiple panes, making split tooltips
  6644. * the preferred layout over
  6645. * the previous `shared` tooltip.
  6646. *
  6647. * @sample highcharts/tooltip/split/
  6648. * Split tooltip
  6649. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  6650. * Split tooltip and custom formatter callback
  6651. *
  6652. * @type {boolean}
  6653. * @default {highcharts} false
  6654. * @default {highstock} true
  6655. * @since 5.0.0
  6656. * @product highcharts highstock
  6657. * @apioption tooltip.split
  6658. */
  6659. /**
  6660. * Prevents the tooltip from switching or closing, when touched or
  6661. * pointed.
  6662. *
  6663. * @sample highcharts/tooltip/stickoncontact/
  6664. * Tooltip sticks on pointer contact
  6665. *
  6666. * @type {boolean}
  6667. * @since 8.0.1
  6668. * @apioption tooltip.stickOnContact
  6669. */
  6670. /**
  6671. * Use HTML to render the contents of the tooltip instead of SVG. Using
  6672. * HTML allows advanced formatting like tables and images in the
  6673. * tooltip. It is also recommended for rtl languages as it works around
  6674. * rtl bugs in early Firefox.
  6675. *
  6676. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  6677. * A table for value alignment
  6678. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  6679. * Full HTML tooltip
  6680. * @sample {highmaps} maps/tooltip/usehtml/
  6681. * Pure HTML tooltip
  6682. *
  6683. * @type {boolean}
  6684. * @default false
  6685. * @since 2.2
  6686. * @apioption tooltip.useHTML
  6687. */
  6688. /**
  6689. * How many decimals to show in each series' y value. This is
  6690. * overridable in each series' tooltip options object. The default is to
  6691. * preserve all decimals.
  6692. *
  6693. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6694. * Set decimals, prefix and suffix for the value
  6695. * @sample {highmaps} maps/tooltip/valuedecimals/
  6696. * Set decimals, prefix and suffix for the value
  6697. *
  6698. * @type {number|undefined}
  6699. * @since 2.2
  6700. * @apioption tooltip.valueDecimals
  6701. */
  6702. /**
  6703. * A string to prepend to each series' y value. Overridable in each
  6704. * series' tooltip options object.
  6705. *
  6706. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6707. * Set decimals, prefix and suffix for the value
  6708. * @sample {highmaps} maps/tooltip/valuedecimals/
  6709. * Set decimals, prefix and suffix for the value
  6710. *
  6711. * @type {string}
  6712. * @since 2.2
  6713. * @apioption tooltip.valuePrefix
  6714. */
  6715. /**
  6716. * A string to append to each series' y value. Overridable in each
  6717. * series' tooltip options object.
  6718. *
  6719. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6720. * Set decimals, prefix and suffix for the value
  6721. * @sample {highmaps} maps/tooltip/valuedecimals/
  6722. * Set decimals, prefix and suffix for the value
  6723. *
  6724. * @type {string}
  6725. * @since 2.2
  6726. * @apioption tooltip.valueSuffix
  6727. */
  6728. /**
  6729. * The format for the date in the tooltip header if the X axis is a
  6730. * datetime axis. The default is a best guess based on the smallest
  6731. * distance between points in the chart.
  6732. *
  6733. * @sample {highcharts} highcharts/tooltip/xdateformat/
  6734. * A different format
  6735. *
  6736. * @type {string}
  6737. * @product highcharts highstock gantt
  6738. * @apioption tooltip.xDateFormat
  6739. */
  6740. /**
  6741. * How many decimals to show for the `point.change`
  6742. * or the `point.cumulativeSum` value when the `series.compare`
  6743. * or the `series.cumulative` option is set.
  6744. * This is overridable in each series' tooltip options object.
  6745. *
  6746. * @type {number}
  6747. * @default 2
  6748. * @since 1.0.1
  6749. * @product highstock
  6750. * @apioption tooltip.changeDecimals
  6751. */
  6752. /**
  6753. * Enable or disable the tooltip.
  6754. *
  6755. * @sample {highcharts} highcharts/tooltip/enabled/
  6756. * Disabled
  6757. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  6758. * Disable tooltip and show values on chart instead
  6759. */
  6760. enabled: true,
  6761. /**
  6762. * Enable or disable animation of the tooltip.
  6763. *
  6764. * @type {boolean}
  6765. * @default true
  6766. * @since 2.3.0
  6767. */
  6768. animation: svg,
  6769. /**
  6770. * The radius of the rounded border corners.
  6771. *
  6772. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6773. * Default border radius
  6774. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  6775. * Square borders
  6776. * @sample {highmaps} maps/tooltip/background-border/
  6777. * Background and border demo
  6778. */
  6779. borderRadius: 3,
  6780. /**
  6781. * For series on datetime axes, the date format in the tooltip's
  6782. * header will by default be guessed based on the closest data points.
  6783. * This member gives the default string representations used for
  6784. * each unit. For an overview of the replacement codes, see
  6785. * [dateFormat](/class-reference/Highcharts.Time#dateFormat).
  6786. *
  6787. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  6788. *
  6789. * @type {Highcharts.Dictionary<string>}
  6790. * @product highcharts highstock gantt
  6791. */
  6792. dateTimeLabelFormats: {
  6793. /** @internal */
  6794. millisecond: '%A, %e %b, %H:%M:%S.%L',
  6795. /** @internal */
  6796. second: '%A, %e %b, %H:%M:%S',
  6797. /** @internal */
  6798. minute: '%A, %e %b, %H:%M',
  6799. /** @internal */
  6800. hour: '%A, %e %b, %H:%M',
  6801. /** @internal */
  6802. day: '%A, %e %b %Y',
  6803. /** @internal */
  6804. week: 'Week from %A, %e %b %Y',
  6805. /** @internal */
  6806. month: '%B %Y',
  6807. /** @internal */
  6808. year: '%Y'
  6809. },
  6810. /**
  6811. * A string to append to the tooltip format.
  6812. *
  6813. * @sample {highcharts} highcharts/tooltip/footerformat/
  6814. * A table for value alignment
  6815. * @sample {highmaps} maps/tooltip/format/
  6816. * Format demo
  6817. *
  6818. * @since 2.2
  6819. */
  6820. footerFormat: '',
  6821. /**
  6822. * The name of a symbol to use for the border around the tooltip
  6823. * header. Applies only when [tooltip.split](#tooltip.split) is
  6824. * enabled.
  6825. *
  6826. * Custom callbacks for symbol path generation can also be added to
  6827. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  6828. * [series.marker.symbol](plotOptions.line.marker.symbol).
  6829. *
  6830. * @see [tooltip.shape](#tooltip.shape)
  6831. *
  6832. * @sample {highstock} stock/tooltip/split-positioner/
  6833. * Different shapes for header and split boxes
  6834. *
  6835. * @type {Highcharts.TooltipShapeValue}
  6836. * @validvalue ["callout", "square"]
  6837. * @since 7.0
  6838. */
  6839. headerShape: 'callout',
  6840. /**
  6841. * The number of milliseconds to wait until the tooltip is hidden when
  6842. * mouse out from a point or chart.
  6843. *
  6844. * @since 3.0
  6845. */
  6846. hideDelay: 500,
  6847. /**
  6848. * Padding inside the tooltip, in pixels.
  6849. *
  6850. * @since 5.0.0
  6851. */
  6852. padding: 8,
  6853. /**
  6854. * The name of a symbol to use for the border around the tooltip. Can
  6855. * be one of: `"callout"`, `"circle"` or `"rect"`. When
  6856. * [tooltip.split](#tooltip.split)
  6857. * option is enabled, shape is applied to all boxes except header, which
  6858. * is controlled by
  6859. * [tooltip.headerShape](#tooltip.headerShape).
  6860. *
  6861. * Custom callbacks for symbol path generation can also be added to
  6862. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  6863. * [series.marker.symbol](plotOptions.line.marker.symbol).
  6864. *
  6865. * @type {Highcharts.TooltipShapeValue}
  6866. * @since 4.0
  6867. */
  6868. shape: 'callout',
  6869. /**
  6870. * When the tooltip is shared, the entire plot area will capture mouse
  6871. * movement or touch events. Tooltip texts for series types with ordered
  6872. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  6873. * This is recommended for single series charts and for tablet/mobile
  6874. * optimized charts.
  6875. *
  6876. * See also [tooltip.split](#tooltip.split), that is better suited for
  6877. * charts with many series, especially line-type series. The
  6878. * `tooltip.split` option takes precedence over `tooltip.shared`.
  6879. *
  6880. * @sample {highcharts} highcharts/tooltip/shared-false/
  6881. * False by default
  6882. * @sample {highcharts} highcharts/tooltip/shared-true/
  6883. * True
  6884. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  6885. * True with x axis crosshair
  6886. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  6887. * True with mixed series types
  6888. *
  6889. * @since 2.1
  6890. * @product highcharts highstock
  6891. */
  6892. shared: false,
  6893. /**
  6894. * Proximity snap for graphs or single points. It defaults to 10 for
  6895. * mouse-powered devices and 25 for touch devices.
  6896. *
  6897. * Note that in most cases the whole plot area captures the mouse
  6898. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  6899. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  6900. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  6901. * or [split](#tooltip.split).
  6902. *
  6903. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6904. * 10 px by default
  6905. * @sample {highcharts} highcharts/tooltip/snap-50/
  6906. * 50 px on graph
  6907. *
  6908. * @type {number}
  6909. * @default 10/25
  6910. * @since 1.2.0
  6911. * @product highcharts highstock
  6912. */
  6913. snap: isTouchDevice ? 25 : 10,
  6914. /**
  6915. * The HTML of the tooltip header line. Variables are enclosed by
  6916. * curly brackets. Available variables are `point.key`, `series.name`,
  6917. * `series.color` and other members from the `point` and `series`
  6918. * objects. The `point.key` variable contains the category name, x
  6919. * value or datetime string depending on the type of axis. For datetime
  6920. * axes, the `point.key` date format can be set using
  6921. * `tooltip.xDateFormat`.
  6922. *
  6923. * @sample {highcharts} highcharts/tooltip/footerformat/
  6924. * An HTML table in the tooltip
  6925. * @sample {highstock} highcharts/tooltip/footerformat/
  6926. * An HTML table in the tooltip
  6927. * @sample {highmaps} maps/tooltip/format/
  6928. * Format demo
  6929. *
  6930. * @type {string}
  6931. * @apioption tooltip.headerFormat
  6932. */
  6933. headerFormat: '<span style="font-size: 0.8em">{point.key}</span><br/>',
  6934. /**
  6935. * The HTML of the null point's line in the tooltip. Works analogously
  6936. * to [pointFormat](#tooltip.pointFormat).
  6937. *
  6938. * @sample {highcharts} highcharts/plotoptions/series-nullformat
  6939. * Format data label and tooltip for null point.
  6940. *
  6941. * @type {string}
  6942. * @apioption tooltip.nullFormat
  6943. */
  6944. /**
  6945. * The HTML of the point's line in the tooltip. Variables are enclosed
  6946. * by curly brackets. Available variables are `point.x`, `point.y`,
  6947. * `series.name` and `series.color` and other properties on the same
  6948. * form. Furthermore, `point.y` can be extended by the
  6949. * `tooltip.valuePrefix` and `tooltip.valueSuffix` variables. This can
  6950. * also be overridden for each series, which makes it a good hook for
  6951. * displaying units.
  6952. *
  6953. * In styled mode, the dot is colored by a class name rather
  6954. * than the point color.
  6955. *
  6956. * @sample {highcharts} highcharts/tooltip/pointformat/
  6957. * A different point format with value suffix
  6958. * @sample {highcharts|highstock} highcharts/tooltip/pointformat-extra-information/
  6959. * Show extra information about points in the tooltip
  6960. * @sample {highmaps} maps/tooltip/format/
  6961. * Format demo
  6962. *
  6963. * @type {string}
  6964. * @since 2.2
  6965. * @apioption tooltip.pointFormat
  6966. */
  6967. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  6968. /**
  6969. * The background color or gradient for the tooltip.
  6970. *
  6971. * In styled mode, the stroke width is set in the
  6972. * `.highcharts-tooltip-box` class.
  6973. *
  6974. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  6975. * Yellowish background
  6976. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  6977. * Gradient
  6978. * @sample {highcharts} highcharts/css/tooltip-border-background/
  6979. * Tooltip in styled mode
  6980. * @sample {highstock} stock/tooltip/general/
  6981. * Custom tooltip
  6982. * @sample {highstock} highcharts/css/tooltip-border-background/
  6983. * Tooltip in styled mode
  6984. * @sample {highmaps} maps/tooltip/background-border/
  6985. * Background and border demo
  6986. * @sample {highmaps} highcharts/css/tooltip-border-background/
  6987. * Tooltip in styled mode
  6988. *
  6989. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6990. */
  6991. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  6992. /**
  6993. * The pixel width of the tooltip border. Defaults to 0 for single
  6994. * tooltips and 1 for split tooltips.
  6995. *
  6996. * In styled mode, the stroke width is set in the
  6997. * `.highcharts-tooltip-box` class.
  6998. *
  6999. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  7000. * 2 pixels
  7001. * @sample {highcharts} highcharts/tooltip/borderwidth/
  7002. * No border (shadow only)
  7003. * @sample {highcharts} highcharts/css/tooltip-border-background/
  7004. * Tooltip in styled mode
  7005. * @sample {highstock} stock/tooltip/general/
  7006. * Custom tooltip
  7007. * @sample {highstock} highcharts/css/tooltip-border-background/
  7008. * Tooltip in styled mode
  7009. * @sample {highmaps} maps/tooltip/background-border/
  7010. * Background and border demo
  7011. * @sample {highmaps} highcharts/css/tooltip-border-background/
  7012. * Tooltip in styled mode
  7013. *
  7014. * @type {number}
  7015. */
  7016. borderWidth: void 0,
  7017. /**
  7018. * Whether to apply a drop shadow to the tooltip.
  7019. *
  7020. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  7021. * True by default
  7022. * @sample {highcharts} highcharts/tooltip/shadow/
  7023. * False
  7024. * @sample {highmaps} maps/tooltip/positioner/
  7025. * Fixed tooltip position, border and shadow disabled
  7026. *
  7027. * @type {boolean|Highcharts.ShadowOptionsObject}
  7028. */
  7029. shadow: true,
  7030. /**
  7031. * Prevents the tooltip from switching or closing when touched or
  7032. * pointed.
  7033. *
  7034. * @sample highcharts/tooltip/stickoncontact/
  7035. * Tooltip sticks on pointer contact
  7036. *
  7037. * @since 8.0.1
  7038. */
  7039. stickOnContact: false,
  7040. /**
  7041. * CSS styles for the tooltip. The tooltip can also be styled through
  7042. * the CSS class `.highcharts-tooltip`.
  7043. *
  7044. * Note that the default `pointerEvents` style makes the tooltip ignore
  7045. * mouse events, so in order to use clickable tooltips, this value must
  7046. * be set to `auto`.
  7047. *
  7048. * @sample {highcharts} highcharts/tooltip/style/
  7049. * Greater padding, bold text
  7050. *
  7051. * @type {Highcharts.CSSObject}
  7052. */
  7053. style: {
  7054. /** @internal */
  7055. color: "#333333" /* Palette.neutralColor80 */,
  7056. /** @internal */
  7057. cursor: 'default',
  7058. /** @internal */
  7059. fontSize: '0.8em'
  7060. },
  7061. /**
  7062. * Use HTML to render the contents of the tooltip instead of SVG. Using
  7063. * HTML allows advanced formatting like tables and images in the
  7064. * tooltip. It is also recommended for rtl languages as it works around
  7065. * rtl bugs in early Firefox.
  7066. *
  7067. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  7068. * A table for value alignment
  7069. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  7070. * Full HTML tooltip
  7071. * @sample {highmaps} maps/tooltip/usehtml/
  7072. * Pure HTML tooltip
  7073. *
  7074. * @since 2.2
  7075. */
  7076. useHTML: false
  7077. },
  7078. /**
  7079. * Highchart by default puts a credits label in the lower right corner
  7080. * of the chart. This can be changed using these options.
  7081. */
  7082. credits: {
  7083. /**
  7084. * Credits for map source to be concatenated with conventional credit
  7085. * text. By default this is a format string that collects copyright
  7086. * information from the map if available.
  7087. *
  7088. * @see [mapTextFull](#credits.mapTextFull)
  7089. * @see [text](#credits.text)
  7090. *
  7091. * @type {string}
  7092. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  7093. * @since 4.2.2
  7094. * @product highmaps
  7095. * @apioption credits.mapText
  7096. */
  7097. /**
  7098. * Detailed credits for map source to be displayed on hover of credits
  7099. * text. By default this is a format string that collects copyright
  7100. * information from the map if available.
  7101. *
  7102. * @see [mapText](#credits.mapText)
  7103. * @see [text](#credits.text)
  7104. *
  7105. * @type {string}
  7106. * @default {geojson.copyright}
  7107. * @since 4.2.2
  7108. * @product highmaps
  7109. * @apioption credits.mapTextFull
  7110. */
  7111. /**
  7112. * Whether to show the credits text.
  7113. *
  7114. * @sample {highcharts} highcharts/credits/enabled-false/
  7115. * Credits disabled
  7116. * @sample {highstock} stock/credits/enabled/
  7117. * Credits disabled
  7118. * @sample {highmaps} maps/credits/enabled-false/
  7119. * Credits disabled
  7120. */
  7121. enabled: true,
  7122. /**
  7123. * The URL for the credits label.
  7124. *
  7125. * @sample {highcharts} highcharts/credits/href/
  7126. * Custom URL and text
  7127. * @sample {highmaps} maps/credits/customized/
  7128. * Custom URL and text
  7129. */
  7130. href: 'https://www.highcharts.com?credits',
  7131. /**
  7132. * Position configuration for the credits label.
  7133. *
  7134. * @sample {highcharts} highcharts/credits/position-left/
  7135. * Left aligned
  7136. * @sample {highcharts} highcharts/credits/position-left/
  7137. * Left aligned
  7138. * @sample {highmaps} maps/credits/customized/
  7139. * Left aligned
  7140. * @sample {highmaps} maps/credits/customized/
  7141. * Left aligned
  7142. *
  7143. * @type {Highcharts.AlignObject}
  7144. * @since 2.1
  7145. */
  7146. position: {
  7147. /** @internal */
  7148. align: 'right',
  7149. /** @internal */
  7150. x: -10,
  7151. /** @internal */
  7152. verticalAlign: 'bottom',
  7153. /** @internal */
  7154. y: -5
  7155. },
  7156. /**
  7157. * CSS styles for the credits label.
  7158. *
  7159. * @see In styled mode, credits styles can be set with the
  7160. * `.highcharts-credits` class.
  7161. *
  7162. * @type {Highcharts.CSSObject}
  7163. */
  7164. style: {
  7165. /** @internal */
  7166. cursor: 'pointer',
  7167. /** @internal */
  7168. color: "#999999" /* Palette.neutralColor40 */,
  7169. /** @internal */
  7170. fontSize: '0.6em'
  7171. },
  7172. /**
  7173. * The text for the credits label.
  7174. *
  7175. * @productdesc {highmaps}
  7176. * If a map is loaded as GeoJSON, the text defaults to
  7177. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  7178. * `Highcharts.com`.
  7179. *
  7180. * @sample {highcharts} highcharts/credits/href/
  7181. * Custom URL and text
  7182. * @sample {highmaps} maps/credits/customized/
  7183. * Custom URL and text
  7184. */
  7185. text: 'Highcharts.com'
  7186. }
  7187. };
  7188. /* eslint-disable spaced-comment */
  7189. defaultOptions.chart.styledMode = false;
  7190. '';
  7191. const defaultTime = new Time(defaultOptions.time);
  7192. /**
  7193. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  7194. * for outside modules wasn't enough because the setOptions method created a new
  7195. * object.
  7196. *
  7197. * @function Highcharts.getOptions
  7198. *
  7199. * @return {Highcharts.Options}
  7200. * Default options.
  7201. */
  7202. function getOptions() {
  7203. return defaultOptions;
  7204. }
  7205. /**
  7206. * Merge the default options with custom options and return the new options
  7207. * structure. Commonly used for defining reusable templates.
  7208. *
  7209. * @sample highcharts/global/useutc-false Setting a global option
  7210. * @sample highcharts/members/setoptions Applying a global theme
  7211. *
  7212. * @function Highcharts.setOptions
  7213. *
  7214. * @param {Highcharts.Options} options
  7215. * The new custom chart options.
  7216. *
  7217. * @return {Highcharts.Options}
  7218. * Updated options.
  7219. */
  7220. function setOptions(options) {
  7221. // Copy in the default options
  7222. merge(true, defaultOptions, options);
  7223. // Update the time object
  7224. if (options.time || options.global) {
  7225. if (H.time) {
  7226. H.time.update(merge(defaultOptions.global, defaultOptions.time, options.global, options.time));
  7227. }
  7228. else {
  7229. /**
  7230. * Global `Time` object with default options. Since v6.0.5, time
  7231. * settings can be applied individually for each chart. If no
  7232. * individual settings apply, this `Time` object is shared by all
  7233. * instances.
  7234. *
  7235. * @name Highcharts.time
  7236. * @type {Highcharts.Time}
  7237. */
  7238. H.time = defaultTime;
  7239. }
  7240. }
  7241. return defaultOptions;
  7242. }
  7243. /* *
  7244. *
  7245. * Default Export
  7246. *
  7247. * */
  7248. const DefaultOptions = {
  7249. defaultOptions,
  7250. defaultTime,
  7251. getOptions,
  7252. setOptions
  7253. };
  7254. /* *
  7255. *
  7256. * API Declarations
  7257. *
  7258. * */
  7259. /**
  7260. * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
  7261. */
  7262. /**
  7263. * Gets fired when a series is added to the chart after load time, using the
  7264. * `addSeries` method. Returning `false` prevents the series from being added.
  7265. *
  7266. * @callback Highcharts.ChartAddSeriesCallbackFunction
  7267. *
  7268. * @param {Highcharts.Chart} this
  7269. * The chart on which the event occured.
  7270. *
  7271. * @param {Highcharts.ChartAddSeriesEventObject} event
  7272. * The event that occured.
  7273. */
  7274. /**
  7275. * Contains common event information. Through the `options` property you can
  7276. * access the series options that were passed to the `addSeries` method.
  7277. *
  7278. * @interface Highcharts.ChartAddSeriesEventObject
  7279. */ /**
  7280. * The series options that were passed to the `addSeries` method.
  7281. * @name Highcharts.ChartAddSeriesEventObject#options
  7282. * @type {Highcharts.SeriesOptionsType}
  7283. */ /**
  7284. * Prevents the default behaviour of the event.
  7285. * @name Highcharts.ChartAddSeriesEventObject#preventDefault
  7286. * @type {Function}
  7287. */ /**
  7288. * The event target.
  7289. * @name Highcharts.ChartAddSeriesEventObject#target
  7290. * @type {Highcharts.Chart}
  7291. */ /**
  7292. * The event type.
  7293. * @name Highcharts.ChartAddSeriesEventObject#type
  7294. * @type {"addSeries"}
  7295. */
  7296. /**
  7297. * Gets fired when clicking on the plot background.
  7298. *
  7299. * @callback Highcharts.ChartClickCallbackFunction
  7300. *
  7301. * @param {Highcharts.Chart} this
  7302. * The chart on which the event occured.
  7303. *
  7304. * @param {Highcharts.PointerEventObject} event
  7305. * The event that occured.
  7306. */
  7307. /**
  7308. * Contains an axes of the clicked spot.
  7309. *
  7310. * @interface Highcharts.ChartClickEventAxisObject
  7311. */ /**
  7312. * Axis at the clicked spot.
  7313. * @name Highcharts.ChartClickEventAxisObject#axis
  7314. * @type {Highcharts.Axis}
  7315. */ /**
  7316. * Axis value at the clicked spot.
  7317. * @name Highcharts.ChartClickEventAxisObject#value
  7318. * @type {number}
  7319. */
  7320. /**
  7321. * Contains information about the clicked spot on the chart. Remember the unit
  7322. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  7323. *
  7324. * @interface Highcharts.ChartClickEventObject
  7325. * @extends Highcharts.PointerEventObject
  7326. */ /**
  7327. * Information about the x-axis on the clicked spot.
  7328. * @name Highcharts.ChartClickEventObject#xAxis
  7329. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  7330. */ /**
  7331. * Information about the y-axis on the clicked spot.
  7332. * @name Highcharts.ChartClickEventObject#yAxis
  7333. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  7334. */ /**
  7335. * Information about the z-axis on the clicked spot.
  7336. * @name Highcharts.ChartClickEventObject#zAxis
  7337. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  7338. */
  7339. /**
  7340. * Gets fired when the chart is finished loading.
  7341. *
  7342. * @callback Highcharts.ChartLoadCallbackFunction
  7343. *
  7344. * @param {Highcharts.Chart} this
  7345. * The chart on which the event occured.
  7346. *
  7347. * @param {global.Event} event
  7348. * The event that occured.
  7349. */
  7350. /**
  7351. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  7352. * after an axis, series or point is modified with the `redraw` option set to
  7353. * `true`.
  7354. *
  7355. * @callback Highcharts.ChartRedrawCallbackFunction
  7356. *
  7357. * @param {Highcharts.Chart} this
  7358. * The chart on which the event occured.
  7359. *
  7360. * @param {global.Event} event
  7361. * The event that occured.
  7362. */
  7363. /**
  7364. * Gets fired after initial load of the chart (directly after the `load` event),
  7365. * and after each redraw (directly after the `redraw` event).
  7366. *
  7367. * @callback Highcharts.ChartRenderCallbackFunction
  7368. *
  7369. * @param {Highcharts.Chart} this
  7370. * The chart on which the event occured.
  7371. *
  7372. * @param {global.Event} event
  7373. * The event that occured.
  7374. */
  7375. /**
  7376. * Gets fired when an area of the chart has been selected. The default action
  7377. * for the selection event is to zoom the chart to the selected area. It can be
  7378. * prevented by calling `event.preventDefault()` or return false.
  7379. *
  7380. * @callback Highcharts.ChartSelectionCallbackFunction
  7381. *
  7382. * @param {Highcharts.Chart} this
  7383. * The chart on which the event occured.
  7384. *
  7385. * @param {Highcharts.SelectEventObject} event
  7386. * Event informations
  7387. *
  7388. * @return {boolean|undefined}
  7389. * Return false to prevent the default action, usually zoom.
  7390. */
  7391. (''); // detach doclets above
  7392. return DefaultOptions;
  7393. });
  7394. _registerModule(_modules, 'Core/Animation/Fx.js', [_modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, H, U) {
  7395. /* *
  7396. *
  7397. * (c) 2010-2021 Torstein Honsi
  7398. *
  7399. * License: www.highcharts.com/license
  7400. *
  7401. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7402. *
  7403. * */
  7404. const { parse: color } = Color;
  7405. const { win } = H;
  7406. const { isNumber, objectEach } = U;
  7407. /* eslint-disable no-invalid-this, valid-jsdoc */
  7408. /* *
  7409. *
  7410. * Class
  7411. *
  7412. * */
  7413. /**
  7414. * An animator object used internally. One instance applies to one property
  7415. * (attribute or style prop) on one element. Animation is always initiated
  7416. * through {@link SVGElement#animate}.
  7417. *
  7418. * @example
  7419. * let rect = renderer.rect(0, 0, 10, 10).add();
  7420. * rect.animate({ width: 100 });
  7421. *
  7422. * @private
  7423. * @class
  7424. * @name Highcharts.Fx
  7425. *
  7426. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  7427. * The element to animate.
  7428. *
  7429. * @param {Partial<Highcharts.AnimationOptionsObject>} options
  7430. * Animation options.
  7431. *
  7432. * @param {string} prop
  7433. * The single attribute or CSS property to animate.
  7434. */
  7435. class Fx {
  7436. /* *
  7437. *
  7438. * Constructors
  7439. *
  7440. * */
  7441. constructor(elem, options, prop) {
  7442. this.pos = NaN;
  7443. this.options = options;
  7444. this.elem = elem;
  7445. this.prop = prop;
  7446. }
  7447. /* *
  7448. *
  7449. * Functions
  7450. *
  7451. * */
  7452. /**
  7453. * Set the current step of a path definition on SVGElement.
  7454. *
  7455. * @function Highcharts.Fx#dSetter
  7456. *
  7457. */
  7458. dSetter() {
  7459. const paths = this.paths, start = paths && paths[0], end = paths && paths[1], now = this.now || 0;
  7460. let path = [];
  7461. // Land on the final path without adjustment points appended in the ends
  7462. if (now === 1 || !start || !end) {
  7463. path = this.toD || [];
  7464. }
  7465. else if (start.length === end.length && now < 1) {
  7466. for (let i = 0; i < end.length; i++) {
  7467. // Tween between the start segment and the end segment. Start
  7468. // with a copy of the end segment and tween the appropriate
  7469. // numerics
  7470. const startSeg = start[i];
  7471. const endSeg = end[i];
  7472. const tweenSeg = [];
  7473. for (let j = 0; j < endSeg.length; j++) {
  7474. const startItem = startSeg[j];
  7475. const endItem = endSeg[j];
  7476. // Tween numbers
  7477. if (isNumber(startItem) &&
  7478. isNumber(endItem) &&
  7479. // Arc boolean flags
  7480. !(endSeg[0] === 'A' && (j === 4 || j === 5))) {
  7481. tweenSeg[j] = startItem + now * (endItem - startItem);
  7482. // Strings, take directly from the end segment
  7483. }
  7484. else {
  7485. tweenSeg[j] = endItem;
  7486. }
  7487. }
  7488. path.push(tweenSeg);
  7489. }
  7490. // If animation is finished or length not matching, land on right value
  7491. }
  7492. else {
  7493. path = end;
  7494. }
  7495. this.elem.attr('d', path, void 0, true);
  7496. }
  7497. /**
  7498. * Update the element with the current animation step.
  7499. *
  7500. * @function Highcharts.Fx#update
  7501. *
  7502. */
  7503. update() {
  7504. const elem = this.elem, prop = this.prop, // if destroyed, it is null
  7505. now = this.now, step = this.options.step;
  7506. // Animation setter defined from outside
  7507. if (this[prop + 'Setter']) {
  7508. this[prop + 'Setter']();
  7509. // Other animations on SVGElement
  7510. }
  7511. else if (elem.attr) {
  7512. if (elem.element) {
  7513. elem.attr(prop, now, null, true);
  7514. }
  7515. // HTML styles, raw HTML content like container size
  7516. }
  7517. else {
  7518. elem.style[prop] = now + this.unit;
  7519. }
  7520. if (step) {
  7521. step.call(elem, now, this);
  7522. }
  7523. }
  7524. /**
  7525. * Run an animation.
  7526. *
  7527. * @function Highcharts.Fx#run
  7528. *
  7529. * @param {number} from
  7530. * The current value, value to start from.
  7531. *
  7532. * @param {number} to
  7533. * The end value, value to land on.
  7534. *
  7535. * @param {string} unit
  7536. * The property unit, for example `px`.
  7537. *
  7538. */
  7539. run(from, to, unit) {
  7540. const self = this, options = self.options, timer = function (gotoEnd) {
  7541. return timer.stopped ? false : self.step(gotoEnd);
  7542. }, requestAnimationFrame = win.requestAnimationFrame ||
  7543. function (step) {
  7544. setTimeout(step, 13);
  7545. }, step = function () {
  7546. for (let i = 0; i < Fx.timers.length; i++) {
  7547. if (!Fx.timers[i]()) {
  7548. Fx.timers.splice(i--, 1);
  7549. }
  7550. }
  7551. if (Fx.timers.length) {
  7552. requestAnimationFrame(step);
  7553. }
  7554. };
  7555. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  7556. delete options.curAnim[this.prop];
  7557. if (options.complete &&
  7558. Object.keys(options.curAnim).length === 0) {
  7559. options.complete.call(this.elem);
  7560. }
  7561. }
  7562. else { // #7166
  7563. this.startTime = +new Date();
  7564. this.start = from;
  7565. this.end = to;
  7566. this.unit = unit;
  7567. this.now = this.start;
  7568. this.pos = 0;
  7569. timer.elem = this.elem;
  7570. timer.prop = this.prop;
  7571. if (timer() && Fx.timers.push(timer) === 1) {
  7572. requestAnimationFrame(step);
  7573. }
  7574. }
  7575. }
  7576. /**
  7577. * Run a single step in the animation.
  7578. *
  7579. * @function Highcharts.Fx#step
  7580. *
  7581. * @param {boolean} [gotoEnd]
  7582. * Whether to go to the endpoint of the animation after abort.
  7583. *
  7584. * @return {boolean}
  7585. * Returns `true` if animation continues.
  7586. */
  7587. step(gotoEnd) {
  7588. const t = +new Date(), options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim;
  7589. let ret, done;
  7590. if ((elem.attr) && !elem.element) { // #2616, element is destroyed
  7591. ret = false;
  7592. }
  7593. else if (gotoEnd || t >= duration + this.startTime) {
  7594. this.now = this.end;
  7595. this.pos = 1;
  7596. this.update();
  7597. curAnim[this.prop] = true;
  7598. done = true;
  7599. objectEach(curAnim, function (val) {
  7600. if (val !== true) {
  7601. done = false;
  7602. }
  7603. });
  7604. if (done && complete) {
  7605. complete.call(elem);
  7606. }
  7607. ret = false;
  7608. }
  7609. else {
  7610. this.pos = options.easing((t - this.startTime) / duration);
  7611. this.now = this.start + ((this.end -
  7612. this.start) * this.pos);
  7613. this.update();
  7614. ret = true;
  7615. }
  7616. return ret;
  7617. }
  7618. /**
  7619. * Prepare start and end values so that the path can be animated one to one.
  7620. *
  7621. * @function Highcharts.Fx#initPath
  7622. *
  7623. * @param {Highcharts.SVGElement} elem
  7624. * The SVGElement item.
  7625. *
  7626. * @param {Highcharts.SVGPathArray|undefined} fromD
  7627. * Starting path definition.
  7628. *
  7629. * @param {Highcharts.SVGPathArray} toD
  7630. * Ending path definition.
  7631. *
  7632. * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
  7633. * An array containing start and end paths in array form so that
  7634. * they can be animated in parallel.
  7635. */
  7636. initPath(elem, fromD, toD) {
  7637. const startX = elem.startX, endX = elem.endX, end = toD.slice(), // copy
  7638. isArea = elem.isArea, positionFactor = isArea ? 2 : 1;
  7639. let shift, fullLength, i, reverse, start = fromD && fromD.slice(); // copy
  7640. if (!start) {
  7641. return [end, end];
  7642. }
  7643. /**
  7644. * If shifting points, prepend a dummy point to the end path.
  7645. * @private
  7646. */
  7647. function prepend(arr, other) {
  7648. while (arr.length < fullLength) {
  7649. // Move to, line to or curve to?
  7650. const moveSegment = arr[0], otherSegment = other[fullLength - arr.length];
  7651. if (otherSegment && moveSegment[0] === 'M') {
  7652. if (otherSegment[0] === 'C') {
  7653. arr[0] = [
  7654. 'C',
  7655. moveSegment[1],
  7656. moveSegment[2],
  7657. moveSegment[1],
  7658. moveSegment[2],
  7659. moveSegment[1],
  7660. moveSegment[2]
  7661. ];
  7662. }
  7663. else {
  7664. arr[0] = ['L', moveSegment[1], moveSegment[2]];
  7665. }
  7666. }
  7667. // Prepend a copy of the first point
  7668. arr.unshift(moveSegment);
  7669. // For areas, the bottom path goes back again to the left, so we
  7670. // need to append a copy of the last point.
  7671. if (isArea) {
  7672. const z = arr.pop();
  7673. arr.push(arr[arr.length - 1], z); // append point and the Z
  7674. }
  7675. }
  7676. }
  7677. /**
  7678. * Copy and append last point until the length matches the end length.
  7679. * @private
  7680. */
  7681. function append(arr, other) {
  7682. while (arr.length < fullLength) {
  7683. // Pull out the slice that is going to be appended or inserted.
  7684. // In a line graph, the positionFactor is 1, and the last point
  7685. // is sliced out. In an area graph, the positionFactor is 2,
  7686. // causing the middle two points to be sliced out, since an area
  7687. // path starts at left, follows the upper path then turns and
  7688. // follows the bottom back.
  7689. const segmentToAdd = arr[Math.floor(arr.length / positionFactor) - 1].slice();
  7690. // Disable the first control point of curve segments
  7691. if (segmentToAdd[0] === 'C') {
  7692. segmentToAdd[1] = segmentToAdd[5];
  7693. segmentToAdd[2] = segmentToAdd[6];
  7694. }
  7695. if (!isArea) {
  7696. arr.push(segmentToAdd);
  7697. }
  7698. else {
  7699. const lowerSegmentToAdd = arr[Math.floor(arr.length / positionFactor)].slice();
  7700. arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
  7701. }
  7702. }
  7703. }
  7704. // For sideways animation, find out how much we need to shift to get the
  7705. // start path Xs to match the end path Xs.
  7706. if (startX && endX && endX.length) {
  7707. for (i = 0; i < startX.length; i++) {
  7708. // Moving left, new points coming in on right
  7709. if (startX[i] === endX[0]) {
  7710. shift = i;
  7711. break;
  7712. // Moving right
  7713. }
  7714. else if (startX[0] ===
  7715. endX[endX.length - startX.length + i]) {
  7716. shift = i;
  7717. reverse = true;
  7718. break;
  7719. // Fixed from the right side, "scaling" left
  7720. }
  7721. else if (startX[startX.length - 1] ===
  7722. endX[endX.length - startX.length + i]) {
  7723. shift = startX.length - i;
  7724. break;
  7725. }
  7726. }
  7727. if (typeof shift === 'undefined') {
  7728. start = [];
  7729. }
  7730. }
  7731. if (start.length && isNumber(shift)) {
  7732. // The common target length for the start and end array, where both
  7733. // arrays are padded in opposite ends
  7734. fullLength = end.length + shift * positionFactor;
  7735. if (!reverse) {
  7736. prepend(end, start);
  7737. append(start, end);
  7738. }
  7739. else {
  7740. prepend(start, end);
  7741. append(end, start);
  7742. }
  7743. }
  7744. return [start, end];
  7745. }
  7746. /**
  7747. * Handle animation of the color attributes directly.
  7748. *
  7749. * @function Highcharts.Fx#fillSetter
  7750. *
  7751. */
  7752. fillSetter() {
  7753. Fx.prototype.strokeSetter.apply(this, arguments);
  7754. }
  7755. /**
  7756. * Handle animation of the color attributes directly.
  7757. *
  7758. * @function Highcharts.Fx#strokeSetter
  7759. *
  7760. */
  7761. strokeSetter() {
  7762. this.elem.attr(this.prop, color(this.start).tweenTo(color(this.end), this.pos), void 0, true);
  7763. }
  7764. }
  7765. /* *
  7766. *
  7767. * Static Properties
  7768. *
  7769. * */
  7770. Fx.timers = [];
  7771. /* *
  7772. *
  7773. * Default Export
  7774. *
  7775. * */
  7776. return Fx;
  7777. });
  7778. _registerModule(_modules, 'Core/Animation/AnimationUtilities.js', [_modules['Core/Animation/Fx.js'], _modules['Core/Utilities.js']], function (Fx, U) {
  7779. /* *
  7780. *
  7781. * (c) 2010-2021 Torstein Honsi
  7782. *
  7783. * License: www.highcharts.com/license
  7784. *
  7785. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7786. *
  7787. * */
  7788. const { defined, getStyle, isArray, isNumber, isObject, merge, objectEach, pick } = U;
  7789. /* *
  7790. *
  7791. * Functions
  7792. *
  7793. * */
  7794. /**
  7795. * Set the global animation to either a given value, or fall back to the given
  7796. * chart's animation option.
  7797. *
  7798. * @function Highcharts.setAnimation
  7799. *
  7800. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>|undefined} animation
  7801. * The animation object.
  7802. *
  7803. * @param {Highcharts.Chart} chart
  7804. * The chart instance.
  7805. *
  7806. * @todo
  7807. * This function always relates to a chart, and sets a property on the renderer,
  7808. * so it should be moved to the SVGRenderer.
  7809. */
  7810. function setAnimation(animation, chart) {
  7811. chart.renderer.globalAnimation = pick(animation, chart.options.chart.animation, true);
  7812. }
  7813. /**
  7814. * Get the animation in object form, where a disabled animation is always
  7815. * returned as `{ duration: 0 }`.
  7816. *
  7817. * @function Highcharts.animObject
  7818. *
  7819. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
  7820. * An animation setting. Can be an object with duration, complete and
  7821. * easing properties, or a boolean to enable or disable.
  7822. *
  7823. * @return {Highcharts.AnimationOptionsObject}
  7824. * An object with at least a duration property.
  7825. */
  7826. function animObject(animation) {
  7827. return isObject(animation) ?
  7828. merge({ duration: 500, defer: 0 }, animation) :
  7829. { duration: animation ? 500 : 0, defer: 0 };
  7830. }
  7831. /**
  7832. * Get the defer as a number value from series animation options.
  7833. *
  7834. * @function Highcharts.getDeferredAnimation
  7835. *
  7836. * @param {Highcharts.Chart} chart
  7837. * The chart instance.
  7838. *
  7839. * @param {boolean|Highcharts.AnimationOptionsObject} animation
  7840. * An animation setting. Can be an object with duration, complete and
  7841. * easing properties, or a boolean to enable or disable.
  7842. *
  7843. * @param {Highcharts.Series} [series]
  7844. * Series to defer animation.
  7845. *
  7846. * @return {number}
  7847. * The numeric value.
  7848. */
  7849. function getDeferredAnimation(chart, animation, series) {
  7850. const labelAnimation = animObject(animation), s = series ? [series] : chart.series;
  7851. let defer = 0, duration = 0;
  7852. s.forEach((series) => {
  7853. const seriesAnim = animObject(series.options.animation);
  7854. defer = animation && defined(animation.defer) ?
  7855. labelAnimation.defer :
  7856. Math.max(defer, seriesAnim.duration + seriesAnim.defer);
  7857. duration = Math.min(labelAnimation.duration, seriesAnim.duration);
  7858. });
  7859. // Disable defer for exporting
  7860. if (chart.renderer.forExport) {
  7861. defer = 0;
  7862. }
  7863. const anim = {
  7864. defer: Math.max(0, defer - duration),
  7865. duration: Math.min(defer, duration)
  7866. };
  7867. return anim;
  7868. }
  7869. /**
  7870. * The global animate method, which uses Fx to create individual animators.
  7871. *
  7872. * @function Highcharts.animate
  7873. *
  7874. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  7875. * The element to animate.
  7876. *
  7877. * @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
  7878. * An object containing key-value pairs of the properties to animate.
  7879. * Supports numeric as pixel-based CSS properties for HTML objects and
  7880. * attributes for SVGElements.
  7881. *
  7882. * @param {Partial<Highcharts.AnimationOptionsObject>} [opt]
  7883. * Animation options.
  7884. *
  7885. * @return {void}
  7886. */
  7887. function animate(el, params, opt) {
  7888. let start, unit = '', end, fx, args;
  7889. if (!isObject(opt)) { // Number or undefined/null
  7890. args = arguments;
  7891. opt = {
  7892. duration: args[2],
  7893. easing: args[3],
  7894. complete: args[4]
  7895. };
  7896. }
  7897. if (!isNumber(opt.duration)) {
  7898. opt.duration = 400;
  7899. }
  7900. opt.easing = typeof opt.easing === 'function' ?
  7901. opt.easing :
  7902. (Math[opt.easing] || Math.easeInOutSine);
  7903. opt.curAnim = merge(params);
  7904. objectEach(params, function (val, prop) {
  7905. // Stop current running animation of this property
  7906. stop(el, prop);
  7907. fx = new Fx(el, opt, prop);
  7908. end = void 0;
  7909. if (prop === 'd' && isArray(params.d)) {
  7910. fx.paths = fx.initPath(el, el.pathArray, params.d);
  7911. fx.toD = params.d;
  7912. start = 0;
  7913. end = 1;
  7914. }
  7915. else if (el.attr) {
  7916. start = el.attr(prop);
  7917. }
  7918. else {
  7919. start = parseFloat(getStyle(el, prop)) || 0;
  7920. if (prop !== 'opacity') {
  7921. unit = 'px';
  7922. }
  7923. }
  7924. if (!end) {
  7925. end = val;
  7926. }
  7927. if (typeof end === 'string' && end.match('px')) {
  7928. end = end.replace(/px/g, ''); // #4351
  7929. }
  7930. fx.run(start, end, unit);
  7931. });
  7932. }
  7933. /**
  7934. * Stop running animation.
  7935. *
  7936. * @function Highcharts.stop
  7937. *
  7938. * @param {Highcharts.SVGElement} el
  7939. * The SVGElement to stop animation on.
  7940. *
  7941. * @param {string} [prop]
  7942. * The property to stop animating. If given, the stop method will stop a
  7943. * single property from animating, while others continue.
  7944. *
  7945. * @return {void}
  7946. *
  7947. * @todo
  7948. * A possible extension to this would be to stop a single property, when
  7949. * we want to continue animating others. Then assign the prop to the timer
  7950. * in the Fx.run method, and check for the prop here. This would be an
  7951. * improvement in all cases where we stop the animation from .attr. Instead of
  7952. * stopping everything, we can just stop the actual attributes we're setting.
  7953. */
  7954. function stop(el, prop) {
  7955. let i = Fx.timers.length;
  7956. // Remove timers related to this element (#4519)
  7957. while (i--) {
  7958. if (Fx.timers[i].elem === el && (!prop || prop === Fx.timers[i].prop)) {
  7959. Fx.timers[i].stopped = true; // #4667
  7960. }
  7961. }
  7962. }
  7963. const animationExports = {
  7964. animate,
  7965. animObject,
  7966. getDeferredAnimation,
  7967. setAnimation,
  7968. stop
  7969. };
  7970. /* *
  7971. *
  7972. * Default Export
  7973. *
  7974. * */
  7975. return animationExports;
  7976. });
  7977. _registerModule(_modules, 'Core/Renderer/HTML/AST.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  7978. /* *
  7979. *
  7980. * (c) 2010-2020 Torstein Honsi
  7981. *
  7982. * License: www.highcharts.com/license
  7983. *
  7984. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7985. *
  7986. * */
  7987. const { SVG_NS, win } = H;
  7988. const { attr, createElement, css, error, isFunction, isString, objectEach, splat } = U;
  7989. const { trustedTypes } = win;
  7990. /* *
  7991. *
  7992. * Constants
  7993. *
  7994. * */
  7995. // Create the trusted type policy. This should not be exposed.
  7996. const trustedTypesPolicy = (trustedTypes &&
  7997. isFunction(trustedTypes.createPolicy) &&
  7998. trustedTypes.createPolicy('highcharts', {
  7999. createHTML: (s) => s
  8000. }));
  8001. const emptyHTML = trustedTypesPolicy ?
  8002. trustedTypesPolicy.createHTML('') :
  8003. '';
  8004. // IE9 and PhantomJS are only able to parse XML.
  8005. const hasValidDOMParser = (function () {
  8006. try {
  8007. return Boolean(new DOMParser().parseFromString(emptyHTML, 'text/html'));
  8008. }
  8009. catch (e) {
  8010. return false;
  8011. }
  8012. }());
  8013. /* *
  8014. *
  8015. * Class
  8016. *
  8017. * */
  8018. /**
  8019. * The AST class represents an abstract syntax tree of HTML or SVG content. It
  8020. * can take HTML as an argument, parse it, optionally transform it to SVG, then
  8021. * perform sanitation before inserting it into the DOM.
  8022. *
  8023. * @class
  8024. * @name Highcharts.AST
  8025. *
  8026. * @param {string|Array<Highcharts.ASTNode>} source
  8027. * Either an HTML string or an ASTNode list to populate the tree.
  8028. */
  8029. class AST {
  8030. /* *
  8031. *
  8032. * Static Functions
  8033. *
  8034. * */
  8035. /**
  8036. * Filter an object of SVG or HTML attributes against the allow list.
  8037. *
  8038. * @static
  8039. *
  8040. * @function Highcharts.AST#filterUserAttributes
  8041. *
  8042. * @param {Highcharts.SVGAttributes} attributes The attributes to filter
  8043. *
  8044. * @return {Highcharts.SVGAttributes}
  8045. * The filtered attributes
  8046. */
  8047. static filterUserAttributes(attributes) {
  8048. objectEach(attributes, (val, key) => {
  8049. let valid = true;
  8050. if (AST.allowedAttributes.indexOf(key) === -1) {
  8051. valid = false;
  8052. }
  8053. if (['background', 'dynsrc', 'href', 'lowsrc', 'src']
  8054. .indexOf(key) !== -1) {
  8055. valid = isString(val) && AST.allowedReferences.some((ref) => val.indexOf(ref) === 0);
  8056. }
  8057. if (!valid) {
  8058. error(33, false, void 0, {
  8059. 'Invalid attribute in config': `${key}`
  8060. });
  8061. delete attributes[key];
  8062. }
  8063. // #17753, < is not allowed in SVG attributes
  8064. if (isString(val) && attributes[key]) {
  8065. attributes[key] = val.replace(/</g, '&lt;');
  8066. }
  8067. });
  8068. return attributes;
  8069. }
  8070. static parseStyle(style) {
  8071. return style
  8072. .split(';')
  8073. .reduce((styles, line) => {
  8074. const pair = line.split(':').map((s) => s.trim()), key = pair.shift();
  8075. if (key && pair.length) {
  8076. styles[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = pair.join(':'); // #17146
  8077. }
  8078. return styles;
  8079. }, {});
  8080. }
  8081. /**
  8082. * Utility function to set html content for an element by passing in a
  8083. * markup string. The markup is safely parsed by the AST class to avoid
  8084. * XSS vulnerabilities. This function should be used instead of setting
  8085. * `innerHTML` in all cases where the content is not fully trusted.
  8086. *
  8087. * @static
  8088. * @function Highcharts.AST#setElementHTML
  8089. *
  8090. * @param {SVGDOMElement|HTMLDOMElement} el
  8091. * Node to set content of.
  8092. *
  8093. * @param {string} html
  8094. * Markup string
  8095. */
  8096. static setElementHTML(el, html) {
  8097. el.innerHTML = AST.emptyHTML; // Clear previous
  8098. if (html) {
  8099. const ast = new AST(html);
  8100. ast.addToDOM(el);
  8101. }
  8102. }
  8103. /* *
  8104. *
  8105. * Constructor
  8106. *
  8107. * */
  8108. // Construct an AST from HTML markup, or wrap an array of existing AST nodes
  8109. constructor(source) {
  8110. this.nodes = typeof source === 'string' ?
  8111. this.parseMarkup(source) : source;
  8112. }
  8113. /* *
  8114. *
  8115. * Functions
  8116. *
  8117. * */
  8118. /**
  8119. * Add the tree defined as a hierarchical JS structure to the DOM
  8120. *
  8121. * @function Highcharts.AST#addToDOM
  8122. *
  8123. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} parent
  8124. * The node where it should be added
  8125. *
  8126. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement}
  8127. * The inserted node.
  8128. */
  8129. addToDOM(parent) {
  8130. /**
  8131. * @private
  8132. * @param {Highcharts.ASTNode} subtree
  8133. * HTML/SVG definition
  8134. * @param {Element} [subParent]
  8135. * parent node
  8136. * @return {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  8137. * The inserted node.
  8138. */
  8139. function recurse(subtree, subParent) {
  8140. let ret;
  8141. splat(subtree).forEach(function (item) {
  8142. const tagName = item.tagName;
  8143. const textNode = item.textContent ?
  8144. H.doc.createTextNode(item.textContent) :
  8145. void 0;
  8146. // Whether to ignore the AST filtering totally, #15345
  8147. const bypassHTMLFiltering = AST.bypassHTMLFiltering;
  8148. let node;
  8149. if (tagName) {
  8150. if (tagName === '#text') {
  8151. node = textNode;
  8152. }
  8153. else if (AST.allowedTags.indexOf(tagName) !== -1 ||
  8154. bypassHTMLFiltering) {
  8155. const NS = tagName === 'svg' ?
  8156. SVG_NS :
  8157. (subParent.namespaceURI || SVG_NS);
  8158. const element = H.doc.createElementNS(NS, tagName);
  8159. const attributes = item.attributes || {};
  8160. // Apply attributes from root of AST node, legacy from
  8161. // from before TextBuilder
  8162. objectEach(item, function (val, key) {
  8163. if (key !== 'tagName' &&
  8164. key !== 'attributes' &&
  8165. key !== 'children' &&
  8166. key !== 'style' &&
  8167. key !== 'textContent') {
  8168. attributes[key] = val;
  8169. }
  8170. });
  8171. attr(element, bypassHTMLFiltering ?
  8172. attributes :
  8173. AST.filterUserAttributes(attributes));
  8174. if (item.style) {
  8175. css(element, item.style);
  8176. }
  8177. // Add text content
  8178. if (textNode) {
  8179. element.appendChild(textNode);
  8180. }
  8181. // Recurse
  8182. recurse(item.children || [], element);
  8183. node = element;
  8184. }
  8185. else {
  8186. error(33, false, void 0, {
  8187. 'Invalid tagName in config': tagName
  8188. });
  8189. }
  8190. }
  8191. // Add to the tree
  8192. if (node) {
  8193. subParent.appendChild(node);
  8194. }
  8195. ret = node;
  8196. });
  8197. // Return last node added (on top level it's the only one)
  8198. return ret;
  8199. }
  8200. return recurse(this.nodes, parent);
  8201. }
  8202. /**
  8203. * Parse HTML/SVG markup into AST Node objects. Used internally from the
  8204. * constructor.
  8205. *
  8206. * @private
  8207. *
  8208. * @function Highcharts.AST#getNodesFromMarkup
  8209. *
  8210. * @param {string} markup The markup string.
  8211. *
  8212. * @return {Array<Highcharts.ASTNode>} The parsed nodes.
  8213. */
  8214. parseMarkup(markup) {
  8215. const nodes = [];
  8216. markup = markup
  8217. .trim()
  8218. // The style attribute throws a warning when parsing when CSP is
  8219. // enabled (#6884), so use an alias and pick it up below
  8220. // Make all quotation marks parse correctly to DOM (#17627)
  8221. .replace(/ style=(["'])/g, ' data-style=$1');
  8222. let doc;
  8223. if (hasValidDOMParser) {
  8224. doc = new DOMParser().parseFromString(trustedTypesPolicy ?
  8225. trustedTypesPolicy.createHTML(markup) :
  8226. markup, 'text/html');
  8227. }
  8228. else {
  8229. const body = createElement('div');
  8230. body.innerHTML = markup;
  8231. doc = { body };
  8232. }
  8233. const appendChildNodes = (node, addTo) => {
  8234. const tagName = node.nodeName.toLowerCase();
  8235. // Add allowed tags
  8236. const astNode = {
  8237. tagName
  8238. };
  8239. if (tagName === '#text') {
  8240. astNode.textContent = node.textContent || '';
  8241. }
  8242. const parsedAttributes = node.attributes;
  8243. // Add attributes
  8244. if (parsedAttributes) {
  8245. const attributes = {};
  8246. [].forEach.call(parsedAttributes, (attrib) => {
  8247. if (attrib.name === 'data-style') {
  8248. astNode.style = AST.parseStyle(attrib.value);
  8249. }
  8250. else {
  8251. attributes[attrib.name] = attrib.value;
  8252. }
  8253. });
  8254. astNode.attributes = attributes;
  8255. }
  8256. // Handle children
  8257. if (node.childNodes.length) {
  8258. const children = [];
  8259. [].forEach.call(node.childNodes, (childNode) => {
  8260. appendChildNodes(childNode, children);
  8261. });
  8262. if (children.length) {
  8263. astNode.children = children;
  8264. }
  8265. }
  8266. addTo.push(astNode);
  8267. };
  8268. [].forEach.call(doc.body.childNodes, (childNode) => appendChildNodes(childNode, nodes));
  8269. return nodes;
  8270. }
  8271. }
  8272. /* *
  8273. *
  8274. * Static Properties
  8275. *
  8276. * */
  8277. /**
  8278. * The list of allowed SVG or HTML attributes, used for sanitizing
  8279. * potentially harmful content from the chart configuration before adding to
  8280. * the DOM.
  8281. *
  8282. * @see [Source code with default values](
  8283. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedAttributes)
  8284. *
  8285. * @example
  8286. * // Allow a custom, trusted attribute
  8287. * Highcharts.AST.allowedAttributes.push('data-value');
  8288. *
  8289. * @name Highcharts.AST.allowedAttributes
  8290. * @type {Array<string>}
  8291. */
  8292. AST.allowedAttributes = [
  8293. 'alt',
  8294. 'aria-controls',
  8295. 'aria-describedby',
  8296. 'aria-expanded',
  8297. 'aria-haspopup',
  8298. 'aria-hidden',
  8299. 'aria-label',
  8300. 'aria-labelledby',
  8301. 'aria-live',
  8302. 'aria-pressed',
  8303. 'aria-readonly',
  8304. 'aria-roledescription',
  8305. 'aria-selected',
  8306. 'class',
  8307. 'clip-path',
  8308. 'color',
  8309. 'colspan',
  8310. 'cx',
  8311. 'cy',
  8312. 'd',
  8313. 'dx',
  8314. 'dy',
  8315. 'disabled',
  8316. 'fill',
  8317. 'flood-color',
  8318. 'flood-opacity',
  8319. 'height',
  8320. 'href',
  8321. 'id',
  8322. 'in',
  8323. 'markerHeight',
  8324. 'markerWidth',
  8325. 'offset',
  8326. 'opacity',
  8327. 'orient',
  8328. 'padding',
  8329. 'paddingLeft',
  8330. 'paddingRight',
  8331. 'patternUnits',
  8332. 'r',
  8333. 'refX',
  8334. 'refY',
  8335. 'role',
  8336. 'scope',
  8337. 'slope',
  8338. 'src',
  8339. 'startOffset',
  8340. 'stdDeviation',
  8341. 'stroke',
  8342. 'stroke-linecap',
  8343. 'stroke-width',
  8344. 'style',
  8345. 'tableValues',
  8346. 'result',
  8347. 'rowspan',
  8348. 'summary',
  8349. 'target',
  8350. 'tabindex',
  8351. 'text-align',
  8352. 'text-anchor',
  8353. 'textAnchor',
  8354. 'textLength',
  8355. 'title',
  8356. 'type',
  8357. 'valign',
  8358. 'width',
  8359. 'x',
  8360. 'x1',
  8361. 'x2',
  8362. 'xlink:href',
  8363. 'y',
  8364. 'y1',
  8365. 'y2',
  8366. 'zIndex'
  8367. ];
  8368. /**
  8369. * The list of allowed references for referring attributes like `href` and
  8370. * `src`. Attribute values will only be allowed if they start with one of
  8371. * these strings.
  8372. *
  8373. * @see [Source code with default values](
  8374. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedReferences)
  8375. *
  8376. * @example
  8377. * // Allow tel:
  8378. * Highcharts.AST.allowedReferences.push('tel:');
  8379. *
  8380. * @name Highcharts.AST.allowedReferences
  8381. * @type {Array<string>}
  8382. */
  8383. AST.allowedReferences = [
  8384. 'https://',
  8385. 'http://',
  8386. 'mailto:',
  8387. '/',
  8388. '../',
  8389. './',
  8390. '#'
  8391. ];
  8392. /**
  8393. * The list of allowed SVG or HTML tags, used for sanitizing potentially
  8394. * harmful content from the chart configuration before adding to the DOM.
  8395. *
  8396. * @see [Source code with default values](
  8397. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedTags)
  8398. *
  8399. * @example
  8400. * // Allow a custom, trusted tag
  8401. * Highcharts.AST.allowedTags.push('blink'); // ;)
  8402. *
  8403. * @name Highcharts.AST.allowedTags
  8404. * @type {Array<string>}
  8405. */
  8406. AST.allowedTags = [
  8407. 'a',
  8408. 'abbr',
  8409. 'b',
  8410. 'br',
  8411. 'button',
  8412. 'caption',
  8413. 'circle',
  8414. 'clipPath',
  8415. 'code',
  8416. 'dd',
  8417. 'defs',
  8418. 'div',
  8419. 'dl',
  8420. 'dt',
  8421. 'em',
  8422. 'feComponentTransfer',
  8423. 'feDropShadow',
  8424. 'feFuncA',
  8425. 'feFuncB',
  8426. 'feFuncG',
  8427. 'feFuncR',
  8428. 'feGaussianBlur',
  8429. 'feOffset',
  8430. 'feMerge',
  8431. 'feMergeNode',
  8432. 'filter',
  8433. 'h1',
  8434. 'h2',
  8435. 'h3',
  8436. 'h4',
  8437. 'h5',
  8438. 'h6',
  8439. 'hr',
  8440. 'i',
  8441. 'img',
  8442. 'li',
  8443. 'linearGradient',
  8444. 'marker',
  8445. 'ol',
  8446. 'p',
  8447. 'path',
  8448. 'pattern',
  8449. 'pre',
  8450. 'rect',
  8451. 'small',
  8452. 'span',
  8453. 'stop',
  8454. 'strong',
  8455. 'style',
  8456. 'sub',
  8457. 'sup',
  8458. 'svg',
  8459. 'table',
  8460. 'text',
  8461. 'textPath',
  8462. 'thead',
  8463. 'title',
  8464. 'tbody',
  8465. 'tspan',
  8466. 'td',
  8467. 'th',
  8468. 'tr',
  8469. 'u',
  8470. 'ul',
  8471. '#text'
  8472. ];
  8473. AST.emptyHTML = emptyHTML;
  8474. /**
  8475. * Allow all custom SVG and HTML attributes, references and tags (together
  8476. * with potentially harmful ones) to be added to the DOM from the chart
  8477. * configuration. In other words, disable the the allow-listing which is the
  8478. * primary functionality of the AST.
  8479. *
  8480. * WARNING: Setting this property to `true` while allowing untrusted user
  8481. * data in the chart configuration will expose your application to XSS
  8482. * security risks!
  8483. *
  8484. * Note that in case you want to allow a known set of tags or attributes,
  8485. * you should allow-list them instead of disabling the filtering totally.
  8486. * See [allowedAttributes](Highcharts.AST#.allowedAttributes),
  8487. * [allowedReferences](Highcharts.AST#.allowedReferences) and
  8488. * [allowedTags](Highcharts.AST#.allowedTags). The `bypassHTMLFiltering`
  8489. * setting is intended only for those cases where allow-listing is not
  8490. * practical, and the chart configuration already comes from a secure
  8491. * source.
  8492. *
  8493. * @example
  8494. * // Allow all custom attributes, references and tags (disable DOM XSS
  8495. * // filtering)
  8496. * Highcharts.AST.bypassHTMLFiltering = true;
  8497. *
  8498. * @name Highcharts.AST.bypassHTMLFiltering
  8499. * @static
  8500. */
  8501. AST.bypassHTMLFiltering = false;
  8502. /* *
  8503. *
  8504. * Default Export
  8505. *
  8506. * */
  8507. /* *
  8508. *
  8509. * API Declarations
  8510. *
  8511. * */
  8512. /**
  8513. * Serialized form of an SVG/HTML definition, including children.
  8514. *
  8515. * @interface Highcharts.ASTNode
  8516. */ /**
  8517. * @name Highcharts.ASTNode#attributes
  8518. * @type {Highcharts.SVGAttributes|undefined}
  8519. */ /**
  8520. * @name Highcharts.ASTNode#children
  8521. * @type {Array<Highcharts.ASTNode>|undefined}
  8522. */ /**
  8523. * @name Highcharts.ASTNode#tagName
  8524. * @type {string|undefined}
  8525. */ /**
  8526. * @name Highcharts.ASTNode#textContent
  8527. * @type {string|undefined}
  8528. */
  8529. (''); // keeps doclets above in file
  8530. return AST;
  8531. });
  8532. _registerModule(_modules, 'Core/Templating.js', [_modules['Core/Defaults.js'], _modules['Core/Utilities.js']], function (D, U) {
  8533. /* *
  8534. *
  8535. * (c) 2010-2021 Torstein Honsi
  8536. *
  8537. * License: www.highcharts.com/license
  8538. *
  8539. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8540. *
  8541. * */
  8542. const { defaultOptions, defaultTime } = D;
  8543. const { extend, getNestedProperty, isArray, isNumber, isObject, isString, pick, pInt } = U;
  8544. const helpers = {
  8545. // Built-in helpers
  8546. add: (a, b) => a + b,
  8547. divide: (a, b) => (b !== 0 ? a / b : ''),
  8548. // eslint-disable-next-line eqeqeq
  8549. eq: (a, b) => a == b,
  8550. each: function (arr) {
  8551. const match = arguments[arguments.length - 1];
  8552. return isArray(arr) ?
  8553. arr.map((item, i) => format(match.body, extend(isObject(item) ? item : { '@this': item }, {
  8554. '@index': i,
  8555. '@first': i === 0,
  8556. '@last': i === arr.length - 1
  8557. }))).join('') :
  8558. false;
  8559. },
  8560. ge: (a, b) => a >= b,
  8561. gt: (a, b) => a > b,
  8562. 'if': (condition) => !!condition,
  8563. le: (a, b) => a <= b,
  8564. lt: (a, b) => a < b,
  8565. multiply: (a, b) => a * b,
  8566. // eslint-disable-next-line eqeqeq
  8567. ne: (a, b) => a != b,
  8568. subtract: (a, b) => a - b,
  8569. unless: (condition) => !condition
  8570. };
  8571. /* *
  8572. *
  8573. * Functions
  8574. *
  8575. * */
  8576. /**
  8577. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  8578. * human readable date string. The format is a subset of the formats for PHP's
  8579. * [strftime](https://www.php.net/manual/en/function.strftime.php) function.
  8580. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  8581. *
  8582. * Since v6.0.5, all internal dates are formatted through the
  8583. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  8584. * The `Highcharts.dateFormat` function only reflects global time settings set
  8585. * with `setOptions`.
  8586. *
  8587. * Supported format keys:
  8588. * - `%a`: Short weekday, like 'Mon'
  8589. * - `%A`: Long weekday, like 'Monday'
  8590. * - `%d`: Two digit day of the month, 01 to 31
  8591. * - `%e`: Day of the month, 1 through 31
  8592. * - `%w`: Day of the week, 0 through 6
  8593. * - `%b`: Short month, like 'Jan'
  8594. * - `%B`: Long month, like 'January'
  8595. * - `%m`: Two digit month number, 01 through 12
  8596. * - `%y`: Two digits year, like 09 for 2009
  8597. * - `%Y`: Four digits year, like 2009
  8598. * - `%H`: Two digits hours in 24h format, 00 through 23
  8599. * - `%k`: Hours in 24h format, 0 through 23
  8600. * - `%I`: Two digits hours in 12h format, 00 through 11
  8601. * - `%l`: Hours in 12h format, 1 through 12
  8602. * - `%M`: Two digits minutes, 00 through 59
  8603. * - `%p`: Upper case AM or PM
  8604. * - `%P`: Lower case AM or PM
  8605. * - `%S`: Two digits seconds, 00 through 59
  8606. * - `%L`: Milliseconds (naming from Ruby)
  8607. *
  8608. * @function Highcharts.dateFormat
  8609. *
  8610. * @param {string} format
  8611. * The desired format where various time representations are prefixed
  8612. * with `%`.
  8613. *
  8614. * @param {number} timestamp
  8615. * The JavaScript timestamp.
  8616. *
  8617. * @param {boolean} [capitalize=false]
  8618. * Upper case first letter in the return.
  8619. *
  8620. * @return {string}
  8621. * The formatted date.
  8622. */
  8623. function dateFormat(format, timestamp, capitalize) {
  8624. return defaultTime.dateFormat(format, timestamp, capitalize);
  8625. }
  8626. /**
  8627. * Format a string according to a subset of the rules of Python's String.format
  8628. * method.
  8629. *
  8630. * @example
  8631. * let s = Highcharts.format(
  8632. * 'The {color} fox was {len:.2f} feet long',
  8633. * { color: 'red', len: Math.PI }
  8634. * );
  8635. * // => The red fox was 3.14 feet long
  8636. *
  8637. * @function Highcharts.format
  8638. *
  8639. * @param {string} str
  8640. * The string to format.
  8641. *
  8642. * @param {Record<string, *>} ctx
  8643. * The context, a collection of key-value pairs where each key is
  8644. * replaced by its value.
  8645. *
  8646. * @param {Highcharts.Chart} [chart]
  8647. * A `Chart` instance used to get numberFormatter and time.
  8648. *
  8649. * @return {string}
  8650. * The formatted string.
  8651. */
  8652. function format(str = '', ctx, chart) {
  8653. const regex = /\{([a-zA-Z0-9\:\.\,;\-\/<>%_@"'= #\(\)]+)\}/g,
  8654. // The sub expression regex is the same as the top expression regex,
  8655. // but except parens and block helpers (#), and surrounded by parens
  8656. // instead of curly brackets.
  8657. subRegex = /\(([a-zA-Z0-9\:\.\,;\-\/<>%_@"'= ]+)\)/g, matches = [], floatRegex = /f$/, decRegex = /\.([0-9])/, lang = defaultOptions.lang, time = chart && chart.time || defaultTime, numberFormatter = chart && chart.numberFormatter || numberFormat;
  8658. /*
  8659. * Get a literal or variable value inside a template expression. May be
  8660. * extended with other types like string or null if needed, but keep it
  8661. * small for now.
  8662. */
  8663. const resolveProperty = (key = '') => {
  8664. let n;
  8665. // Literals
  8666. if (key === 'true') {
  8667. return true;
  8668. }
  8669. if (key === 'false') {
  8670. return false;
  8671. }
  8672. if ((n = Number(key)).toString() === key) {
  8673. return n;
  8674. }
  8675. // Variables and constants
  8676. return getNestedProperty(key, ctx);
  8677. };
  8678. let match, currentMatch, depth = 0, hasSub;
  8679. // Parse and create tree
  8680. while ((match = regex.exec(str)) !== null) {
  8681. // When a sub expression is found, it is evaluated first, and the
  8682. // results recursively evaluated until no subexpression exists.
  8683. const subMatch = subRegex.exec(match[1]);
  8684. if (subMatch) {
  8685. match = subMatch;
  8686. hasSub = true;
  8687. }
  8688. if (!currentMatch || !currentMatch.isBlock) {
  8689. currentMatch = {
  8690. ctx,
  8691. expression: match[1],
  8692. find: match[0],
  8693. isBlock: match[1].charAt(0) === '#',
  8694. start: match.index,
  8695. startInner: match.index + match[0].length,
  8696. length: match[0].length
  8697. };
  8698. }
  8699. // Identify helpers
  8700. const fn = match[1].split(' ')[0].replace('#', '');
  8701. if (helpers[fn]) {
  8702. // Block helper, only 0 level is handled
  8703. if (currentMatch.isBlock && fn === currentMatch.fn) {
  8704. depth++;
  8705. }
  8706. if (!currentMatch.fn) {
  8707. currentMatch.fn = fn;
  8708. }
  8709. }
  8710. // Closing a block helper
  8711. const startingElseSection = match[1] === 'else';
  8712. if (currentMatch.isBlock &&
  8713. currentMatch.fn && (match[1] === `/${currentMatch.fn}` ||
  8714. startingElseSection)) {
  8715. if (!depth) { // === 0
  8716. const start = currentMatch.startInner, body = str.substr(start, match.index - start);
  8717. // Either closing without an else section, or when encountering
  8718. // an else section
  8719. if (currentMatch.body === void 0) {
  8720. currentMatch.body = body;
  8721. currentMatch.startInner = match.index + match[0].length;
  8722. // The body exists already, so this is the else section
  8723. }
  8724. else {
  8725. currentMatch.elseBody = body;
  8726. }
  8727. currentMatch.find += body + match[0];
  8728. if (!startingElseSection) {
  8729. matches.push(currentMatch);
  8730. currentMatch = void 0;
  8731. }
  8732. }
  8733. else if (!startingElseSection) {
  8734. depth--;
  8735. }
  8736. // Common expression
  8737. }
  8738. else if (!currentMatch.isBlock) {
  8739. matches.push(currentMatch);
  8740. }
  8741. // Evaluate sub-matches one by one to prevent orphaned block closers
  8742. if (subMatch && !(currentMatch === null || currentMatch === void 0 ? void 0 : currentMatch.isBlock)) {
  8743. break;
  8744. }
  8745. }
  8746. // Execute
  8747. matches.forEach((match) => {
  8748. const { body, elseBody, expression, fn } = match;
  8749. let replacement, i;
  8750. // Helper function
  8751. if (fn) {
  8752. // Pass the helpers the amount of arguments defined by the function,
  8753. // then the match as the last argument.
  8754. const args = [match], parts = expression.split(' ');
  8755. i = helpers[fn].length;
  8756. while (i--) {
  8757. args.unshift(resolveProperty(parts[i + 1]));
  8758. }
  8759. replacement = helpers[fn].apply(ctx, args);
  8760. // Block helpers may return true or false. They may also return a
  8761. // string, like the `each` helper.
  8762. if (match.isBlock && typeof replacement === 'boolean') {
  8763. replacement = format(replacement ? body : elseBody, ctx);
  8764. }
  8765. // Simple variable replacement
  8766. }
  8767. else {
  8768. const valueAndFormat = expression.split(':');
  8769. replacement = resolveProperty(valueAndFormat.shift() || '');
  8770. // Format the replacement
  8771. if (valueAndFormat.length && typeof replacement === 'number') {
  8772. const segment = valueAndFormat.join(':');
  8773. if (floatRegex.test(segment)) { // float
  8774. const decimals = parseInt((segment.match(decRegex) || ['', '-1'])[1], 10);
  8775. if (replacement !== null) {
  8776. replacement = numberFormatter(replacement, decimals, lang.decimalPoint, segment.indexOf(',') > -1 ? lang.thousandsSep : '');
  8777. }
  8778. }
  8779. else {
  8780. replacement = time.dateFormat(segment, replacement);
  8781. }
  8782. }
  8783. }
  8784. str = str.replace(match.find, pick(replacement, ''));
  8785. });
  8786. return hasSub ? format(str, ctx, chart) : str;
  8787. }
  8788. /**
  8789. * Format a number and return a string based on input settings.
  8790. *
  8791. * @sample highcharts/members/highcharts-numberformat/
  8792. * Custom number format
  8793. *
  8794. * @function Highcharts.numberFormat
  8795. *
  8796. * @param {number} number
  8797. * The input number to format.
  8798. *
  8799. * @param {number} decimals
  8800. * The amount of decimals. A value of -1 preserves the amount in the
  8801. * input number.
  8802. *
  8803. * @param {string} [decimalPoint]
  8804. * The decimal point, defaults to the one given in the lang options, or
  8805. * a dot.
  8806. *
  8807. * @param {string} [thousandsSep]
  8808. * The thousands separator, defaults to the one given in the lang
  8809. * options, or a space character.
  8810. *
  8811. * @return {string}
  8812. * The formatted number.
  8813. */
  8814. function numberFormat(number, decimals, decimalPoint, thousandsSep) {
  8815. number = +number || 0;
  8816. decimals = +decimals;
  8817. let ret, fractionDigits;
  8818. const lang = defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, exponent = number.toString().split('e'), firstDecimals = decimals;
  8819. if (decimals === -1) {
  8820. // Preserve decimals. Not huge numbers (#3793).
  8821. decimals = Math.min(origDec, 20);
  8822. }
  8823. else if (!isNumber(decimals)) {
  8824. decimals = 2;
  8825. }
  8826. else if (decimals && exponent[1] && exponent[1] < 0) {
  8827. // Expose decimals from exponential notation (#7042)
  8828. fractionDigits = decimals + +exponent[1];
  8829. if (fractionDigits >= 0) {
  8830. // remove too small part of the number while keeping the notation
  8831. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  8832. .split('e')[0];
  8833. decimals = fractionDigits;
  8834. }
  8835. else {
  8836. // fractionDigits < 0
  8837. exponent[0] = exponent[0].split('.')[0] || 0;
  8838. if (decimals < 20) {
  8839. // use number instead of exponential notation (#7405)
  8840. number = (exponent[0] * Math.pow(10, exponent[1]))
  8841. .toFixed(decimals);
  8842. }
  8843. else {
  8844. // or zero
  8845. number = 0;
  8846. }
  8847. exponent[1] = 0;
  8848. }
  8849. }
  8850. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  8851. // Then use toFixed to handle rounding.
  8852. const roundedNumber = (Math.abs(exponent[1] ? exponent[0] : number) +
  8853. Math.pow(10, -Math.max(decimals, origDec) - 1)).toFixed(decimals);
  8854. // A string containing the positive integer component of the number
  8855. const strinteger = String(pInt(roundedNumber));
  8856. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  8857. const thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  8858. // Language
  8859. decimalPoint = pick(decimalPoint, lang.decimalPoint);
  8860. thousandsSep = pick(thousandsSep, lang.thousandsSep);
  8861. // Start building the return
  8862. ret = number < 0 ? '-' : '';
  8863. // Add the leftover after grouping into thousands. For example, in the
  8864. // number 42 000 000, this line adds 42.
  8865. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  8866. if (+exponent[1] < 0 && !firstDecimals) {
  8867. ret = '0';
  8868. }
  8869. else {
  8870. // Add the remaining thousands groups, joined by the thousands separator
  8871. ret += strinteger
  8872. .substr(thousands)
  8873. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  8874. }
  8875. // Add the decimal point and the decimal component
  8876. if (decimals) {
  8877. // Get the decimal component
  8878. ret += decimalPoint + roundedNumber.slice(-decimals);
  8879. }
  8880. if (exponent[1] && +ret !== 0) {
  8881. ret += 'e' + exponent[1];
  8882. }
  8883. return ret;
  8884. }
  8885. /* *
  8886. *
  8887. * Default Export
  8888. *
  8889. * */
  8890. const Templating = {
  8891. dateFormat,
  8892. format,
  8893. helpers,
  8894. numberFormat
  8895. };
  8896. return Templating;
  8897. });
  8898. _registerModule(_modules, 'Core/Renderer/RendererUtilities.js', [_modules['Core/Utilities.js']], function (U) {
  8899. /* *
  8900. *
  8901. * (c) 2010-2021 Torstein Honsi
  8902. *
  8903. * License: www.highcharts.com/license
  8904. *
  8905. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8906. *
  8907. * */
  8908. /* *
  8909. *
  8910. * Imports
  8911. *
  8912. * */
  8913. const { clamp, pick, stableSort } = U;
  8914. /* *
  8915. *
  8916. * Namespace
  8917. *
  8918. * */
  8919. var RendererUtilities;
  8920. (function (RendererUtilities) {
  8921. /* *
  8922. *
  8923. * Declarations
  8924. *
  8925. * */
  8926. /* *
  8927. *
  8928. * Functions
  8929. *
  8930. * */
  8931. /* eslint-disable valid-jsdoc */
  8932. /**
  8933. * General distribution algorithm for distributing labels of differing size
  8934. * along a confined length in two dimensions. The algorithm takes an array
  8935. * of objects containing a size, a target and a rank. It will place the
  8936. * labels as close as possible to their targets, skipping the lowest ranked
  8937. * labels if necessary.
  8938. * @private
  8939. */
  8940. function distribute(boxes, len, maxDistance) {
  8941. // Original array will be altered with added .pos
  8942. const origBoxes = boxes, reducedLen = origBoxes.reducedLen || len, sortByRank = (a, b) => (b.rank || 0) - (a.rank || 0), sortByTarget = (a, b) => a.target - b.target;
  8943. let i, overlapping = true, restBoxes = [], // The outranked overshoot
  8944. box, target, total = 0;
  8945. // If the total size exceeds the len, remove those boxes with the lowest
  8946. // rank
  8947. i = boxes.length;
  8948. while (i--) {
  8949. total += boxes[i].size;
  8950. }
  8951. // Sort by rank, then slice away overshoot
  8952. if (total > reducedLen) {
  8953. stableSort(boxes, sortByRank);
  8954. i = 0;
  8955. total = 0;
  8956. while (total <= reducedLen) {
  8957. total += boxes[i].size;
  8958. i++;
  8959. }
  8960. restBoxes = boxes.splice(i - 1, boxes.length);
  8961. }
  8962. // Order by target
  8963. stableSort(boxes, sortByTarget);
  8964. // So far we have been mutating the original array. Now
  8965. // create a copy with target arrays
  8966. boxes = boxes.map((box) => ({
  8967. size: box.size,
  8968. targets: [box.target],
  8969. align: pick(box.align, 0.5)
  8970. }));
  8971. while (overlapping) {
  8972. // Initial positions: target centered in box
  8973. i = boxes.length;
  8974. while (i--) {
  8975. box = boxes[i];
  8976. // Composite box, average of targets
  8977. target = (Math.min.apply(0, box.targets) +
  8978. Math.max.apply(0, box.targets)) / 2;
  8979. box.pos = clamp(target - box.size * box.align, 0, len - box.size);
  8980. }
  8981. // Detect overlap and join boxes
  8982. i = boxes.length;
  8983. overlapping = false;
  8984. while (i--) {
  8985. // Overlap
  8986. if (i > 0 &&
  8987. boxes[i - 1].pos + boxes[i - 1].size >
  8988. boxes[i].pos) {
  8989. // Add this size to the previous box
  8990. boxes[i - 1].size += boxes[i].size;
  8991. boxes[i - 1].targets = boxes[i - 1]
  8992. .targets
  8993. .concat(boxes[i].targets);
  8994. boxes[i - 1].align = 0.5;
  8995. // Overlapping right, push left
  8996. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  8997. boxes[i - 1].pos = len - boxes[i - 1].size;
  8998. }
  8999. boxes.splice(i, 1); // Remove this item
  9000. overlapping = true;
  9001. }
  9002. }
  9003. }
  9004. // Add the rest (hidden boxes)
  9005. origBoxes.push.apply(origBoxes, restBoxes);
  9006. // Now the composite boxes are placed, we need to put the original boxes
  9007. // within them
  9008. i = 0;
  9009. boxes.some((box) => {
  9010. let posInCompositeBox = 0;
  9011. // Exceeded maxDistance => abort
  9012. return (box.targets || []).some(() => {
  9013. origBoxes[i].pos = box.pos + posInCompositeBox;
  9014. // If the distance between the position and the target exceeds
  9015. // maxDistance, abort the loop and decrease the length in
  9016. // increments of 10% to recursively reduce the number of
  9017. // visible boxes by rank. Once all boxes are within the
  9018. // maxDistance, we're good.
  9019. if (typeof maxDistance !== 'undefined' &&
  9020. Math.abs(origBoxes[i].pos - origBoxes[i].target) > maxDistance) {
  9021. // Reset the positions that are already set
  9022. origBoxes
  9023. .slice(0, i + 1)
  9024. .forEach((box) => delete box.pos);
  9025. // Try with a smaller length
  9026. origBoxes.reducedLen =
  9027. (origBoxes.reducedLen || len) - (len * 0.1);
  9028. // Recurse
  9029. if (origBoxes.reducedLen > len * 0.1) {
  9030. distribute(origBoxes, len, maxDistance);
  9031. }
  9032. // Exceeded maxDistance => abort
  9033. return true;
  9034. }
  9035. posInCompositeBox += origBoxes[i].size;
  9036. i++;
  9037. return false;
  9038. });
  9039. });
  9040. // Add the rest (hidden) boxes and sort by target
  9041. stableSort(origBoxes, sortByTarget);
  9042. return origBoxes;
  9043. }
  9044. RendererUtilities.distribute = distribute;
  9045. })(RendererUtilities || (RendererUtilities = {}));
  9046. /* *
  9047. *
  9048. * Default Export
  9049. *
  9050. * */
  9051. return RendererUtilities;
  9052. });
  9053. _registerModule(_modules, 'Core/Renderer/SVG/SVGElement.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (A, Color, H, U) {
  9054. /* *
  9055. *
  9056. * (c) 2010-2021 Torstein Honsi
  9057. *
  9058. * License: www.highcharts.com/license
  9059. *
  9060. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9061. *
  9062. * */
  9063. const { animate, animObject, stop } = A;
  9064. const { deg2rad, doc, noop, svg, SVG_NS, win } = H;
  9065. const { addEvent, attr, createElement, css, defined, erase, extend, fireEvent, isArray, isFunction, isObject, isString, merge, objectEach, pick, pInt, syncTimeout, uniqueKey } = U;
  9066. /* *
  9067. *
  9068. * Class
  9069. *
  9070. * */
  9071. /* eslint-disable no-invalid-this, valid-jsdoc */
  9072. /**
  9073. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  9074. * rendering layer of Highcharts. Combined with the
  9075. * {@link Highcharts.SVGRenderer}
  9076. * object, these prototypes allow freeform annotation in the charts or even in
  9077. * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
  9078. * labels, when `text` or `label` elements are created with the `useHTML`
  9079. * parameter.
  9080. *
  9081. * The SVGElement instances are created through factory functions on the
  9082. * {@link Highcharts.SVGRenderer}
  9083. * object, like
  9084. * {@link Highcharts.SVGRenderer#rect|rect},
  9085. * {@link Highcharts.SVGRenderer#path|path},
  9086. * {@link Highcharts.SVGRenderer#text|text},
  9087. * {@link Highcharts.SVGRenderer#label|label},
  9088. * {@link Highcharts.SVGRenderer#g|g}
  9089. * and more.
  9090. *
  9091. * @class
  9092. * @name Highcharts.SVGElement
  9093. */
  9094. class SVGElement {
  9095. constructor() {
  9096. /* *
  9097. *
  9098. * Properties
  9099. *
  9100. * */
  9101. this.element = void 0;
  9102. this.onEvents = {};
  9103. this.opacity = 1; // Default base for animation
  9104. this.renderer = void 0;
  9105. this.SVG_NS = SVG_NS;
  9106. }
  9107. // @todo public zIndex?: number;
  9108. /* *
  9109. *
  9110. * Functions
  9111. *
  9112. * */
  9113. /**
  9114. * Get the current value of an attribute or pseudo attribute,
  9115. * used mainly for animation. Called internally from
  9116. * the {@link Highcharts.SVGRenderer#attr} function.
  9117. *
  9118. * @private
  9119. * @function Highcharts.SVGElement#_defaultGetter
  9120. *
  9121. * @param {string} key
  9122. * Property key.
  9123. *
  9124. * @return {number|string}
  9125. * Property value.
  9126. */
  9127. _defaultGetter(key) {
  9128. let ret = pick(this[key + 'Value'], // align getter
  9129. this[key], this.element ? this.element.getAttribute(key) : null, 0);
  9130. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  9131. ret = parseFloat(ret);
  9132. }
  9133. return ret;
  9134. }
  9135. /**
  9136. * @private
  9137. * @function Highcharts.SVGElement#_defaultSetter
  9138. *
  9139. * @param {string} value
  9140. *
  9141. * @param {string} key
  9142. *
  9143. * @param {Highcharts.SVGDOMElement} element
  9144. *
  9145. */
  9146. _defaultSetter(value, key, element) {
  9147. element.setAttribute(key, value);
  9148. }
  9149. /**
  9150. * Add the element to the DOM. All elements must be added this way.
  9151. *
  9152. * @sample highcharts/members/renderer-g
  9153. * Elements added to a group
  9154. *
  9155. * @function Highcharts.SVGElement#add
  9156. *
  9157. * @param {Highcharts.SVGElement} [parent]
  9158. * The parent item to add it to. If undefined, the element is added
  9159. * to the {@link Highcharts.SVGRenderer.box}.
  9160. *
  9161. * @return {Highcharts.SVGElement}
  9162. * Returns the SVGElement for chaining.
  9163. */
  9164. add(parent) {
  9165. const renderer = this.renderer, element = this.element;
  9166. let inserted;
  9167. if (parent) {
  9168. this.parentGroup = parent;
  9169. }
  9170. // Build formatted text
  9171. if (typeof this.textStr !== 'undefined' &&
  9172. this.element.nodeName === 'text' // Not for SVGLabel instances
  9173. ) {
  9174. renderer.buildText(this);
  9175. }
  9176. // Mark as added
  9177. this.added = true;
  9178. // If we're adding to renderer root, or other elements in the group
  9179. // have a z index, we need to handle it
  9180. if (!parent || parent.handleZ || this.zIndex) {
  9181. inserted = this.zIndexSetter();
  9182. }
  9183. // If zIndex is not handled, append at the end
  9184. if (!inserted) {
  9185. (parent ?
  9186. parent.element :
  9187. renderer.box).appendChild(element);
  9188. }
  9189. // fire an event for internal hooks
  9190. if (this.onAdd) {
  9191. this.onAdd();
  9192. }
  9193. return this;
  9194. }
  9195. /**
  9196. * Add a class name to an element.
  9197. *
  9198. * @function Highcharts.SVGElement#addClass
  9199. *
  9200. * @param {string} className
  9201. * The new class name to add.
  9202. *
  9203. * @param {boolean} [replace=false]
  9204. * When true, the existing class name(s) will be overwritten with the new
  9205. * one. When false, the new one is added.
  9206. *
  9207. * @return {Highcharts.SVGElement}
  9208. * Return the SVG element for chainability.
  9209. */
  9210. addClass(className, replace) {
  9211. const currentClassName = replace ? '' : (this.attr('class') || '');
  9212. // Trim the string and remove duplicates
  9213. className = (className || '')
  9214. .split(/ /g)
  9215. .reduce(function (newClassName, name) {
  9216. if (currentClassName.indexOf(name) === -1) {
  9217. newClassName.push(name);
  9218. }
  9219. return newClassName;
  9220. }, (currentClassName ?
  9221. [currentClassName] :
  9222. []))
  9223. .join(' ');
  9224. if (className !== currentClassName) {
  9225. this.attr('class', className);
  9226. }
  9227. return this;
  9228. }
  9229. /**
  9230. * This method is executed in the end of `attr()`, after setting all
  9231. * attributes in the hash. In can be used to efficiently consolidate
  9232. * multiple attributes in one SVG property -- e.g., translate, rotate and
  9233. * scale are merged in one "transform" attribute in the SVG node.
  9234. *
  9235. * @private
  9236. * @function Highcharts.SVGElement#afterSetters
  9237. */
  9238. afterSetters() {
  9239. // Update transform. Do this outside the loop to prevent redundant
  9240. // updating for batch setting of attributes.
  9241. if (this.doTransform) {
  9242. this.updateTransform();
  9243. this.doTransform = false;
  9244. }
  9245. }
  9246. /**
  9247. * Align the element relative to the chart or another box.
  9248. *
  9249. * @function Highcharts.SVGElement#align
  9250. *
  9251. * @param {Highcharts.AlignObject} [alignOptions]
  9252. * The alignment options. The function can be called without this
  9253. * parameter in order to re-align an element after the box has been
  9254. * updated.
  9255. *
  9256. * @param {boolean} [alignByTranslate]
  9257. * Align element by translation.
  9258. *
  9259. * @param {string|Highcharts.BBoxObject} [box]
  9260. * The box to align to, needs a width and height. When the box is a
  9261. * string, it refers to an object in the Renderer. For example, when
  9262. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  9263. * holds `width`, `height`, `x` and `y` properties.
  9264. *
  9265. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  9266. */
  9267. align(alignOptions, alignByTranslate, box) {
  9268. const attribs = {}, renderer = this.renderer, alignedObjects = renderer.alignedObjects;
  9269. let x, y, alignTo, alignFactor, vAlignFactor;
  9270. // First call on instanciate
  9271. if (alignOptions) {
  9272. this.alignOptions = alignOptions;
  9273. this.alignByTranslate = alignByTranslate;
  9274. if (!box || isString(box)) {
  9275. this.alignTo = alignTo = box || 'renderer';
  9276. // prevent duplicates, like legendGroup after resize
  9277. erase(alignedObjects, this);
  9278. alignedObjects.push(this);
  9279. box = void 0; // reassign it below
  9280. }
  9281. // When called on resize, no arguments are supplied
  9282. }
  9283. else {
  9284. alignOptions = this.alignOptions;
  9285. alignByTranslate = this.alignByTranslate;
  9286. alignTo = this.alignTo;
  9287. }
  9288. box = pick(box, renderer[alignTo], alignTo === 'scrollablePlotBox' ?
  9289. renderer.plotBox : void 0, renderer);
  9290. // Assign variables
  9291. const align = alignOptions.align, vAlign = alignOptions.verticalAlign;
  9292. // default: left align
  9293. x = (box.x || 0) + (alignOptions.x || 0);
  9294. // default: top align
  9295. y = (box.y || 0) + (alignOptions.y || 0);
  9296. // Align
  9297. if (align === 'right') {
  9298. alignFactor = 1;
  9299. }
  9300. else if (align === 'center') {
  9301. alignFactor = 2;
  9302. }
  9303. if (alignFactor) {
  9304. x += (box.width - (alignOptions.width || 0)) /
  9305. alignFactor;
  9306. }
  9307. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  9308. // Vertical align
  9309. if (vAlign === 'bottom') {
  9310. vAlignFactor = 1;
  9311. }
  9312. else if (vAlign === 'middle') {
  9313. vAlignFactor = 2;
  9314. }
  9315. if (vAlignFactor) {
  9316. y += (box.height - (alignOptions.height || 0)) /
  9317. vAlignFactor;
  9318. }
  9319. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  9320. // Animate only if already placed
  9321. this[this.placed ? 'animate' : 'attr'](attribs);
  9322. this.placed = true;
  9323. this.alignAttr = attribs;
  9324. return this;
  9325. }
  9326. /**
  9327. * @private
  9328. * @function Highcharts.SVGElement#alignSetter
  9329. * @param {"left"|"center"|"right"} value
  9330. */
  9331. alignSetter(value) {
  9332. const convert = {
  9333. left: 'start',
  9334. center: 'middle',
  9335. right: 'end'
  9336. };
  9337. if (convert[value]) {
  9338. this.alignValue = value;
  9339. this.element.setAttribute('text-anchor', convert[value]);
  9340. }
  9341. }
  9342. /**
  9343. * Animate to given attributes or CSS properties.
  9344. *
  9345. * @sample highcharts/members/element-on/
  9346. * Setting some attributes by animation
  9347. *
  9348. * @function Highcharts.SVGElement#animate
  9349. *
  9350. * @param {Highcharts.SVGAttributes} params
  9351. * SVG attributes or CSS to animate.
  9352. *
  9353. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [options]
  9354. * Animation options.
  9355. *
  9356. * @param {Function} [complete]
  9357. * Function to perform at the end of animation.
  9358. *
  9359. * @return {Highcharts.SVGElement}
  9360. * Returns the SVGElement for chaining.
  9361. */
  9362. animate(params, options, complete) {
  9363. const animOptions = animObject(pick(options, this.renderer.globalAnimation, true)), deferTime = animOptions.defer;
  9364. // When the page is hidden save resources in the background by not
  9365. // running animation at all (#9749).
  9366. if (doc.hidden) {
  9367. animOptions.duration = 0;
  9368. }
  9369. if (animOptions.duration !== 0) {
  9370. // allows using a callback with the global animation without
  9371. // overwriting it
  9372. if (complete) {
  9373. animOptions.complete = complete;
  9374. }
  9375. // If defer option is defined delay the animation #12901
  9376. syncTimeout(() => {
  9377. if (this.element) {
  9378. animate(this, params, animOptions);
  9379. }
  9380. }, deferTime);
  9381. }
  9382. else {
  9383. this.attr(params, void 0, complete || animOptions.complete);
  9384. // Call the end step synchronously
  9385. objectEach(params, function (val, prop) {
  9386. if (animOptions.step) {
  9387. animOptions.step.call(this, val, { prop: prop, pos: 1, elem: this });
  9388. }
  9389. }, this);
  9390. }
  9391. return this;
  9392. }
  9393. /**
  9394. * Apply a text outline through a custom CSS property, by copying the text
  9395. * element and apply stroke to the copy. Used internally. Contrast checks at
  9396. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  9397. *
  9398. * @example
  9399. * // Specific color
  9400. * text.css({
  9401. * textOutline: '1px black'
  9402. * });
  9403. * // Automatic contrast
  9404. * text.css({
  9405. * color: '#000000', // black text
  9406. * textOutline: '1px contrast' // => white outline
  9407. * });
  9408. *
  9409. * @private
  9410. * @function Highcharts.SVGElement#applyTextOutline
  9411. *
  9412. * @param {string} textOutline
  9413. * A custom CSS `text-outline` setting, defined by `width color`.
  9414. */
  9415. applyTextOutline(textOutline) {
  9416. const elem = this.element, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {};
  9417. // When the text shadow is set to contrast, use dark stroke for light
  9418. // text and vice versa.
  9419. if (hasContrast) {
  9420. styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
  9421. }
  9422. // Extract the stroke width and color
  9423. const parts = textOutline.split(' ');
  9424. const color = parts[parts.length - 1];
  9425. let strokeWidth = parts[0];
  9426. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  9427. this.fakeTS = true; // Fake text shadow
  9428. // Since the stroke is applied on center of the actual outline, we
  9429. // need to double it to get the correct stroke-width outside the
  9430. // glyphs.
  9431. strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
  9432. return (2 * Number(digit)) + unit;
  9433. });
  9434. // Remove shadows from previous runs.
  9435. this.removeTextOutline();
  9436. const outline = doc.createElementNS(SVG_NS, 'tspan');
  9437. attr(outline, {
  9438. 'class': 'highcharts-text-outline',
  9439. fill: color,
  9440. stroke: color,
  9441. 'stroke-width': strokeWidth,
  9442. 'stroke-linejoin': 'round'
  9443. });
  9444. // For each of the tspans and text nodes, create a copy in the
  9445. // outline.
  9446. const parentElem = elem.querySelector('textPath') || elem;
  9447. [].forEach.call(parentElem.childNodes, (childNode) => {
  9448. const clone = childNode.cloneNode(true);
  9449. if (clone.removeAttribute) {
  9450. ['fill', 'stroke', 'stroke-width', 'stroke'].forEach((prop) => clone
  9451. .removeAttribute(prop));
  9452. }
  9453. outline.appendChild(clone);
  9454. });
  9455. // Collect the sum of dy from all children, included nested ones
  9456. let totalHeight = 0;
  9457. [].forEach.call(parentElem.querySelectorAll('text tspan'), (element) => {
  9458. totalHeight += Number(element.getAttribute('dy'));
  9459. });
  9460. // Insert an absolutely positioned break before the original text
  9461. // to keep it in place
  9462. const br = doc.createElementNS(SVG_NS, 'tspan');
  9463. br.textContent = '\u200B';
  9464. // Reset the position for the following text
  9465. attr(br, {
  9466. x: Number(elem.getAttribute('x')),
  9467. dy: -totalHeight
  9468. });
  9469. // Insert the outline
  9470. outline.appendChild(br);
  9471. parentElem.insertBefore(outline, parentElem.firstChild);
  9472. }
  9473. }
  9474. /**
  9475. * @function Highcharts.SVGElement#attr
  9476. * @param {string} key
  9477. * @return {number|string}
  9478. */ /**
  9479. * Apply native and custom attributes to the SVG elements.
  9480. *
  9481. * In order to set the rotation center for rotation, set x and y to 0 and
  9482. * use `translateX` and `translateY` attributes to position the element
  9483. * instead.
  9484. *
  9485. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  9486. * `stroke-width`.
  9487. *
  9488. * @sample highcharts/members/renderer-rect/
  9489. * Setting some attributes
  9490. *
  9491. * @example
  9492. * // Set multiple attributes
  9493. * element.attr({
  9494. * stroke: 'red',
  9495. * fill: 'blue',
  9496. * x: 10,
  9497. * y: 10
  9498. * });
  9499. *
  9500. * // Set a single attribute
  9501. * element.attr('stroke', 'red');
  9502. *
  9503. * // Get an attribute
  9504. * element.attr('stroke'); // => 'red'
  9505. *
  9506. * @function Highcharts.SVGElement#attr
  9507. *
  9508. * @param {string|Highcharts.SVGAttributes} [hash]
  9509. * The native and custom SVG attributes.
  9510. *
  9511. * @param {number|string|Highcharts.SVGPathArray} [val]
  9512. * If the type of the first argument is `string`, the second can be a
  9513. * value, which will serve as a single attribute setter. If the first
  9514. * argument is a string and the second is undefined, the function
  9515. * serves as a getter and the current value of the property is
  9516. * returned.
  9517. *
  9518. * @param {Function} [complete]
  9519. * A callback function to execute after setting the attributes. This
  9520. * makes the function compliant and interchangeable with the
  9521. * {@link SVGElement#animate} function.
  9522. *
  9523. * @param {boolean} [continueAnimation=true]
  9524. * Used internally when `.attr` is called as part of an animation
  9525. * step. Otherwise, calling `.attr` for an attribute will stop
  9526. * animation for that attribute.
  9527. *
  9528. * @return {Highcharts.SVGElement}
  9529. * If used as a setter, it returns the current
  9530. * {@link Highcharts.SVGElement} so the calls can be chained. If
  9531. * used as a getter, the current value of the attribute is returned.
  9532. */
  9533. attr(hash, val, complete, continueAnimation) {
  9534. const element = this.element, symbolCustomAttribs = SVGElement.symbolCustomAttribs;
  9535. let key, hasSetSymbolSize, ret = this, skipAttr, setter;
  9536. // single key-value pair
  9537. if (typeof hash === 'string' && typeof val !== 'undefined') {
  9538. key = hash;
  9539. hash = {};
  9540. hash[key] = val;
  9541. }
  9542. // used as a getter: first argument is a string, second is undefined
  9543. if (typeof hash === 'string') {
  9544. ret = (this[hash + 'Getter'] ||
  9545. this._defaultGetter).call(this, hash, element);
  9546. // setter
  9547. }
  9548. else {
  9549. objectEach(hash, function eachAttribute(val, key) {
  9550. skipAttr = false;
  9551. // Unless .attr is from the animator update, stop current
  9552. // running animation of this property
  9553. if (!continueAnimation) {
  9554. stop(this, key);
  9555. }
  9556. // Special handling of symbol attributes
  9557. if (this.symbolName &&
  9558. symbolCustomAttribs.indexOf(key) !== -1) {
  9559. if (!hasSetSymbolSize) {
  9560. this.symbolAttr(hash);
  9561. hasSetSymbolSize = true;
  9562. }
  9563. skipAttr = true;
  9564. }
  9565. if (this.rotation && (key === 'x' || key === 'y')) {
  9566. this.doTransform = true;
  9567. }
  9568. if (!skipAttr) {
  9569. setter = (this[key + 'Setter'] ||
  9570. this._defaultSetter);
  9571. setter.call(this, val, key, element);
  9572. }
  9573. }, this);
  9574. this.afterSetters();
  9575. }
  9576. // In accordance with animate, run a complete callback
  9577. if (complete) {
  9578. complete.call(this);
  9579. }
  9580. return ret;
  9581. }
  9582. /**
  9583. * Apply a clipping rectangle to this element.
  9584. *
  9585. * @function Highcharts.SVGElement#clip
  9586. *
  9587. * @param {Highcharts.ClipRectElement} [clipRect]
  9588. * The clipping rectangle. If skipped, the current clip is removed.
  9589. *
  9590. * @return {Highcharts.SVGElement}
  9591. * Returns the SVG element to allow chaining.
  9592. */
  9593. clip(clipRect) {
  9594. return this.attr('clip-path', clipRect ?
  9595. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  9596. 'none');
  9597. }
  9598. /**
  9599. * Calculate the coordinates needed for drawing a rectangle crisply and
  9600. * return the calculated attributes.
  9601. *
  9602. * @function Highcharts.SVGElement#crisp
  9603. *
  9604. * @param {Highcharts.RectangleObject} rect
  9605. * Rectangle to crisp.
  9606. *
  9607. * @param {number} [strokeWidth]
  9608. * The stroke width to consider when computing crisp positioning. It can
  9609. * also be set directly on the rect parameter.
  9610. *
  9611. * @return {Highcharts.RectangleObject}
  9612. * The modified rectangle arguments.
  9613. */
  9614. crisp(rect, strokeWidth) {
  9615. const wrapper = this;
  9616. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  9617. // Math.round because strokeWidth can sometimes have roundoff errors
  9618. const normalizer = Math.round(strokeWidth) % 2 / 2;
  9619. // normalize for crisp edges
  9620. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  9621. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  9622. rect.width = Math.floor((rect.width || wrapper.width || 0) - 2 * normalizer);
  9623. rect.height = Math.floor((rect.height || wrapper.height || 0) - 2 * normalizer);
  9624. if (defined(rect.strokeWidth)) {
  9625. rect.strokeWidth = strokeWidth;
  9626. }
  9627. return rect;
  9628. }
  9629. /**
  9630. * Build and apply an SVG gradient out of a common JavaScript configuration
  9631. * object. This function is called from the attribute setters. An event
  9632. * hook is added for supporting other complex color types.
  9633. *
  9634. * @private
  9635. * @function Highcharts.SVGElement#complexColor
  9636. *
  9637. * @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
  9638. * The gradient or pattern options structure.
  9639. *
  9640. * @param {string} prop
  9641. * The property to apply, can either be `fill` or `stroke`.
  9642. *
  9643. * @param {Highcharts.SVGDOMElement} elem
  9644. * SVG element to apply the gradient on.
  9645. */
  9646. complexColor(colorOptions, prop, elem) {
  9647. const renderer = this.renderer;
  9648. let colorObject, gradName, gradAttr, radAttr, gradients, stops, stopColor, stopOpacity, radialReference, id, key = [], value;
  9649. fireEvent(this.renderer, 'complexColor', {
  9650. args: arguments
  9651. }, function () {
  9652. // Apply linear or radial gradients
  9653. if (colorOptions.radialGradient) {
  9654. gradName = 'radialGradient';
  9655. }
  9656. else if (colorOptions.linearGradient) {
  9657. gradName = 'linearGradient';
  9658. }
  9659. if (gradName) {
  9660. gradAttr = colorOptions[gradName];
  9661. gradients = renderer.gradients;
  9662. stops = colorOptions.stops;
  9663. radialReference = elem.radialReference;
  9664. // Keep < 2.2 kompatibility
  9665. if (isArray(gradAttr)) {
  9666. colorOptions[gradName] = gradAttr = {
  9667. x1: gradAttr[0],
  9668. y1: gradAttr[1],
  9669. x2: gradAttr[2],
  9670. y2: gradAttr[3],
  9671. gradientUnits: 'userSpaceOnUse'
  9672. };
  9673. }
  9674. // Correct the radial gradient for the radial reference system
  9675. if (gradName === 'radialGradient' &&
  9676. radialReference &&
  9677. !defined(gradAttr.gradientUnits)) {
  9678. // Save the radial attributes for updating
  9679. radAttr = gradAttr;
  9680. gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
  9681. }
  9682. // Build the unique key to detect whether we need to create a
  9683. // new element (#1282)
  9684. objectEach(gradAttr, function (value, n) {
  9685. if (n !== 'id') {
  9686. key.push(n, value);
  9687. }
  9688. });
  9689. objectEach(stops, function (val) {
  9690. key.push(val);
  9691. });
  9692. key = key.join(',');
  9693. // Check if a gradient object with the same config object is
  9694. // created within this renderer
  9695. if (gradients[key]) {
  9696. id = gradients[key].attr('id');
  9697. }
  9698. else {
  9699. // Set the id and create the element
  9700. gradAttr.id = id = uniqueKey();
  9701. const gradientObject = gradients[key] =
  9702. renderer.createElement(gradName)
  9703. .attr(gradAttr)
  9704. .add(renderer.defs);
  9705. gradientObject.radAttr = radAttr;
  9706. // The gradient needs to keep a list of stops to be able to
  9707. // destroy them
  9708. gradientObject.stops = [];
  9709. stops.forEach(function (stop) {
  9710. if (stop[1].indexOf('rgba') === 0) {
  9711. colorObject = Color.parse(stop[1]);
  9712. stopColor = colorObject.get('rgb');
  9713. stopOpacity = colorObject.get('a');
  9714. }
  9715. else {
  9716. stopColor = stop[1];
  9717. stopOpacity = 1;
  9718. }
  9719. const stopObject = renderer.createElement('stop').attr({
  9720. offset: stop[0],
  9721. 'stop-color': stopColor,
  9722. 'stop-opacity': stopOpacity
  9723. }).add(gradientObject);
  9724. // Add the stop element to the gradient
  9725. gradientObject.stops.push(stopObject);
  9726. });
  9727. }
  9728. // Set the reference to the gradient object
  9729. value = 'url(' + renderer.url + '#' + id + ')';
  9730. elem.setAttribute(prop, value);
  9731. elem.gradient = key;
  9732. // Allow the color to be concatenated into tooltips formatters
  9733. // etc. (#2995)
  9734. colorOptions.toString = function () {
  9735. return value;
  9736. };
  9737. }
  9738. });
  9739. }
  9740. /**
  9741. * Set styles for the element. In addition to CSS styles supported by
  9742. * native SVG and HTML elements, there are also some custom made for
  9743. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  9744. * elements.
  9745. *
  9746. * @sample highcharts/members/renderer-text-on-chart/
  9747. * Styled text
  9748. *
  9749. * @function Highcharts.SVGElement#css
  9750. *
  9751. * @param {Highcharts.CSSObject} styles
  9752. * The new CSS styles.
  9753. *
  9754. * @return {Highcharts.SVGElement}
  9755. * Return the SVG element for chaining.
  9756. */
  9757. css(styles) {
  9758. const oldStyles = this.styles, newStyles = {}, elem = this.element;
  9759. let textWidth, hasNew = !oldStyles;
  9760. // Filter out existing styles to increase performance (#2640)
  9761. if (oldStyles) {
  9762. objectEach(styles, function (value, n) {
  9763. if (oldStyles && oldStyles[n] !== value) {
  9764. newStyles[n] = value;
  9765. hasNew = true;
  9766. }
  9767. });
  9768. }
  9769. if (hasNew) {
  9770. // Merge the new styles with the old ones
  9771. if (oldStyles) {
  9772. styles = extend(oldStyles, newStyles);
  9773. }
  9774. // Get the text width from style
  9775. // Previously set, unset it (#8234)
  9776. if (styles.width === null || styles.width === 'auto') {
  9777. delete this.textWidth;
  9778. // Apply new
  9779. }
  9780. else if (elem.nodeName.toLowerCase() === 'text' &&
  9781. styles.width) {
  9782. textWidth = this.textWidth = pInt(styles.width);
  9783. }
  9784. // store object
  9785. this.styles = styles;
  9786. if (textWidth && (!svg && this.renderer.forExport)) {
  9787. delete styles.width;
  9788. }
  9789. const stylesToApply = merge(styles);
  9790. if (elem.namespaceURI === this.SVG_NS) {
  9791. // These CSS properties are interpreted internally by the SVG
  9792. // renderer, but are not supported by SVG and should not be
  9793. // added to the DOM. In styled mode, no CSS should find its way
  9794. // to the DOM whatsoever (#6173, #6474).
  9795. ['textOutline', 'textOverflow', 'width'].forEach((key) => (stylesToApply &&
  9796. delete stylesToApply[key]));
  9797. // SVG requires fill for text
  9798. if (stylesToApply.color) {
  9799. stylesToApply.fill = stylesToApply.color;
  9800. }
  9801. }
  9802. css(elem, stylesToApply);
  9803. }
  9804. if (this.added) {
  9805. // Rebuild text after added. Cache mechanisms in the buildText will
  9806. // prevent building if there are no significant changes.
  9807. if (this.element.nodeName === 'text') {
  9808. this.renderer.buildText(this);
  9809. }
  9810. // Apply text outline after added
  9811. if (styles.textOutline) {
  9812. this.applyTextOutline(styles.textOutline);
  9813. }
  9814. }
  9815. return this;
  9816. }
  9817. /**
  9818. * @private
  9819. * @function Highcharts.SVGElement#dashstyleSetter
  9820. * @param {string} value
  9821. */
  9822. dashstyleSetter(value) {
  9823. let i, strokeWidth = this['stroke-width'];
  9824. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  9825. // strokeWidth function, we should be able to use that instead.
  9826. if (strokeWidth === 'inherit') {
  9827. strokeWidth = 1;
  9828. }
  9829. value = value && value.toLowerCase();
  9830. if (value) {
  9831. const v = value
  9832. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  9833. .replace('shortdashdot', '3,1,1,1')
  9834. .replace('shortdot', '1,1,')
  9835. .replace('shortdash', '3,1,')
  9836. .replace('longdash', '8,3,')
  9837. .replace(/dot/g, '1,3,')
  9838. .replace('dash', '4,3,')
  9839. .replace(/,$/, '')
  9840. .split(','); // ending comma
  9841. i = v.length;
  9842. while (i--) {
  9843. v[i] = '' + (pInt(v[i]) * pick(strokeWidth, NaN));
  9844. }
  9845. value = v.join(',').replace(/NaN/g, 'none'); // #3226
  9846. this.element.setAttribute('stroke-dasharray', value);
  9847. }
  9848. }
  9849. /**
  9850. * Destroy the element and element wrapper and clear up the DOM and event
  9851. * hooks.
  9852. *
  9853. * @function Highcharts.SVGElement#destroy
  9854. */
  9855. destroy() {
  9856. const wrapper = this, element = wrapper.element || {}, renderer = wrapper.renderer, ownerSVGElement = element.ownerSVGElement;
  9857. let parentToClean = (element.nodeName === 'SPAN' &&
  9858. wrapper.parentGroup ||
  9859. void 0), grandParent, i;
  9860. // remove events
  9861. element.onclick = element.onmouseout = element.onmouseover =
  9862. element.onmousemove = element.point = null;
  9863. stop(wrapper); // stop running animations
  9864. if (wrapper.clipPath && ownerSVGElement) {
  9865. const clipPath = wrapper.clipPath;
  9866. // Look for existing references to this clipPath and remove them
  9867. // before destroying the element (#6196).
  9868. // The upper case version is for Edge
  9869. [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
  9870. if (el.getAttribute('clip-path').indexOf(clipPath.element.id) > -1) {
  9871. el.removeAttribute('clip-path');
  9872. }
  9873. });
  9874. wrapper.clipPath = clipPath.destroy();
  9875. }
  9876. // Destroy stops in case this is a gradient object @todo old code?
  9877. if (wrapper.stops) {
  9878. for (i = 0; i < wrapper.stops.length; i++) {
  9879. wrapper.stops[i].destroy();
  9880. }
  9881. wrapper.stops.length = 0;
  9882. wrapper.stops = void 0;
  9883. }
  9884. // remove element
  9885. wrapper.safeRemoveChild(element);
  9886. // In case of useHTML, clean up empty containers emulating SVG groups
  9887. // (#1960, #2393, #2697).
  9888. while (parentToClean &&
  9889. parentToClean.div &&
  9890. parentToClean.div.childNodes.length === 0) {
  9891. grandParent = parentToClean.parentGroup;
  9892. wrapper.safeRemoveChild(parentToClean.div);
  9893. delete parentToClean.div;
  9894. parentToClean = grandParent;
  9895. }
  9896. // remove from alignObjects
  9897. if (wrapper.alignTo) {
  9898. erase(renderer.alignedObjects, wrapper);
  9899. }
  9900. objectEach(wrapper, function (val, key) {
  9901. // Destroy child elements of a group
  9902. if (wrapper[key] &&
  9903. wrapper[key].parentGroup === wrapper &&
  9904. wrapper[key].destroy) {
  9905. wrapper[key].destroy();
  9906. }
  9907. // Delete all properties
  9908. delete wrapper[key];
  9909. });
  9910. return;
  9911. }
  9912. /**
  9913. * @private
  9914. * @function Highcharts.SVGElement#dSettter
  9915. * @param {number|string|Highcharts.SVGPathArray} value
  9916. * @param {string} key
  9917. * @param {Highcharts.SVGDOMElement} element
  9918. */
  9919. dSetter(value, key, element) {
  9920. if (isArray(value)) {
  9921. // Backwards compatibility, convert one-dimensional array into an
  9922. // array of segments
  9923. if (typeof value[0] === 'string') {
  9924. value = this.renderer.pathToSegments(value);
  9925. }
  9926. this.pathArray = value;
  9927. value = value.reduce((acc, seg, i) => {
  9928. if (!seg || !seg.join) {
  9929. return (seg || '').toString();
  9930. }
  9931. return (i ? acc + ' ' : '') + seg.join(' ');
  9932. }, '');
  9933. }
  9934. if (/(NaN| {2}|^$)/.test(value)) {
  9935. value = 'M 0 0';
  9936. }
  9937. // Check for cache before resetting. Resetting causes disturbance in the
  9938. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  9939. // possible performance gain.
  9940. if (this[key] !== value) {
  9941. element.setAttribute(key, value);
  9942. this[key] = value;
  9943. }
  9944. }
  9945. /**
  9946. * Fade out an element by animating its opacity down to 0, and hide it on
  9947. * complete. Used internally for the tooltip.
  9948. *
  9949. * @function Highcharts.SVGElement#fadeOut
  9950. *
  9951. * @param {number} [duration=150]
  9952. * The fade duration in milliseconds.
  9953. */
  9954. fadeOut(duration) {
  9955. const elemWrapper = this;
  9956. elemWrapper.animate({
  9957. opacity: 0
  9958. }, {
  9959. duration: pick(duration, 150),
  9960. complete: function () {
  9961. // #3088, assuming we're only using this for tooltips
  9962. elemWrapper.hide();
  9963. }
  9964. });
  9965. }
  9966. /**
  9967. * @private
  9968. * @function Highcharts.SVGElement#fillSetter
  9969. * @param {Highcharts.ColorType} value
  9970. * @param {string} key
  9971. * @param {Highcharts.SVGDOMElement} element
  9972. */
  9973. fillSetter(value, key, element) {
  9974. if (typeof value === 'string') {
  9975. element.setAttribute(key, value);
  9976. }
  9977. else if (value) {
  9978. this.complexColor(value, key, element);
  9979. }
  9980. }
  9981. /**
  9982. * Get the bounding box (width, height, x and y) for the element. Generally
  9983. * used to get rendered text size. Since this is called a lot in charts,
  9984. * the results are cached based on text properties, in order to save DOM
  9985. * traffic. The returned bounding box includes the rotation, so for example
  9986. * a single text line of rotation 90 will report a greater height, and a
  9987. * width corresponding to the line-height.
  9988. *
  9989. * @sample highcharts/members/renderer-on-chart/
  9990. * Draw a rectangle based on a text's bounding box
  9991. *
  9992. * @function Highcharts.SVGElement#getBBox
  9993. *
  9994. * @param {boolean} [reload]
  9995. * Skip the cache and get the updated DOM bouding box.
  9996. *
  9997. * @param {number} [rot]
  9998. * Override the element's rotation. This is internally used on axis
  9999. * labels with a value of 0 to find out what the bounding box would
  10000. * be have been if it were not rotated.
  10001. *
  10002. * @return {Highcharts.BBoxObject}
  10003. * The bounding box with `x`, `y`, `width` and `height` properties.
  10004. */
  10005. getBBox(reload, rot) {
  10006. const wrapper = this, { alignValue, element, renderer, styles, textStr } = wrapper, { cache, cacheKeys } = renderer, isSVG = element.namespaceURI === wrapper.SVG_NS, rotation = pick(rot, wrapper.rotation, 0), fontSize = renderer.styledMode ? (element &&
  10007. SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles && styles.fontSize);
  10008. let bBox, width, height, toggleTextShadowShim, cacheKey;
  10009. // Avoid undefined and null (#7316)
  10010. if (defined(textStr)) {
  10011. cacheKey = textStr.toString();
  10012. // Since numbers are monospaced, and numerical labels appear a lot
  10013. // in a chart, we assume that a label of n characters has the same
  10014. // bounding box as others of the same length. Unless there is inner
  10015. // HTML in the label. In that case, leave the numbers as is (#5899).
  10016. if (cacheKey.indexOf('<') === -1) {
  10017. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  10018. }
  10019. // Properties that affect bounding box
  10020. cacheKey += [
  10021. '',
  10022. renderer.rootFontSize,
  10023. fontSize,
  10024. rotation,
  10025. wrapper.textWidth,
  10026. alignValue,
  10027. styles && styles.textOverflow,
  10028. styles && styles.fontWeight // #12163
  10029. ].join(',');
  10030. }
  10031. if (cacheKey && !reload) {
  10032. bBox = cache[cacheKey];
  10033. }
  10034. // No cache found
  10035. if (!bBox) {
  10036. // SVG elements
  10037. if (isSVG || renderer.forExport) {
  10038. try { // Fails in Firefox if the container has display: none.
  10039. // When the text shadow shim is used, we need to hide the
  10040. // fake shadows to get the correct bounding box (#3872)
  10041. toggleTextShadowShim = this.fakeTS && function (display) {
  10042. const outline = element.querySelector('.highcharts-text-outline');
  10043. if (outline) {
  10044. css(outline, { display });
  10045. }
  10046. };
  10047. // Workaround for #3842, Firefox reporting wrong bounding
  10048. // box for shadows
  10049. if (isFunction(toggleTextShadowShim)) {
  10050. toggleTextShadowShim('none');
  10051. }
  10052. bBox = element.getBBox ?
  10053. // SVG: use extend because IE9 is not allowed to change
  10054. // width and height in case of rotation (below)
  10055. extend({}, element.getBBox()) : {
  10056. // HTML elements with `exporting.allowHTML` and
  10057. // legacy IE in export mode
  10058. width: element.offsetWidth,
  10059. height: element.offsetHeight,
  10060. x: 0,
  10061. y: 0
  10062. };
  10063. // #3842
  10064. if (isFunction(toggleTextShadowShim)) {
  10065. toggleTextShadowShim('');
  10066. }
  10067. }
  10068. catch (e) {
  10069. '';
  10070. }
  10071. // If the bBox is not set, the try-catch block above failed. The
  10072. // other condition is for Opera that returns a width of
  10073. // -Infinity on hidden elements.
  10074. if (!bBox || bBox.width < 0) {
  10075. bBox = { x: 0, y: 0, width: 0, height: 0 };
  10076. }
  10077. // useHTML within SVG
  10078. }
  10079. else {
  10080. bBox = wrapper.htmlGetBBox();
  10081. }
  10082. // True SVG elements as well as HTML elements in modern browsers
  10083. // using the .useHTML option need to compensated for rotation
  10084. width = bBox.width;
  10085. height = bBox.height;
  10086. // Workaround for wrong bounding box in IE, Edge and Chrome on
  10087. // Windows. With Highcharts' default font, IE and Edge report
  10088. // a box height of 16.899 and Chrome rounds it to 17. If this
  10089. // stands uncorrected, it results in more padding added below
  10090. // the text than above when adding a label border or background.
  10091. // Also vertical positioning is affected.
  10092. // https://jsfiddle.net/highcharts/em37nvuj/
  10093. // (#1101, #1505, #1669, #2568, #6213).
  10094. if (isSVG) {
  10095. bBox.height = height = ({
  10096. '11px,17': 14,
  10097. '13px,20': 16
  10098. }[`${fontSize || ''},${Math.round(height)}`] ||
  10099. height);
  10100. }
  10101. // Adjust for rotated text
  10102. if (rotation) {
  10103. const baseline = Number(element.getAttribute('y') || 0) - bBox.y, alignFactor = {
  10104. 'right': 1,
  10105. 'center': 0.5
  10106. }[alignValue || 0] || 0, rad = rotation * deg2rad, rad90 = (rotation - 90) * deg2rad, wCosRad = width * Math.cos(rad), wSinRad = width * Math.sin(rad), cosRad90 = Math.cos(rad90), sinRad90 = Math.sin(rad90),
  10107. // Find the starting point on the left side baseline of
  10108. // the text
  10109. pX = bBox.x + alignFactor * (width - wCosRad), pY = bBox.y + baseline - alignFactor * wSinRad,
  10110. // Find all corners
  10111. aX = pX + baseline * cosRad90, bX = aX + wCosRad, cX = bX - height * cosRad90, dX = cX - wCosRad, aY = pY + baseline * sinRad90, bY = aY + wSinRad, cY = bY - height * sinRad90, dY = cY - wSinRad;
  10112. // Deduct the bounding box from the corners
  10113. bBox.x = Math.min(aX, bX, cX, dX);
  10114. bBox.y = Math.min(aY, bY, cY, dY);
  10115. bBox.width = Math.max(aX, bX, cX, dX) - bBox.x;
  10116. bBox.height = Math.max(aY, bY, cY, dY) - bBox.y;
  10117. }
  10118. }
  10119. // Cache it. When loading a chart in a hidden iframe in Firefox and
  10120. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  10121. if (cacheKey && (textStr === '' || bBox.height > 0)) {
  10122. // Rotate (#4681)
  10123. while (cacheKeys.length > 250) {
  10124. delete cache[cacheKeys.shift()];
  10125. }
  10126. if (!cache[cacheKey]) {
  10127. cacheKeys.push(cacheKey);
  10128. }
  10129. cache[cacheKey] = bBox;
  10130. }
  10131. return bBox;
  10132. }
  10133. /**
  10134. * Get the computed style. Only in styled mode.
  10135. *
  10136. * @example
  10137. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  10138. *
  10139. * @function Highcharts.SVGElement#getStyle
  10140. *
  10141. * @param {string} prop
  10142. * The property name to check for.
  10143. *
  10144. * @return {string}
  10145. * The current computed value.
  10146. */
  10147. getStyle(prop) {
  10148. return win
  10149. .getComputedStyle(this.element || this, '')
  10150. .getPropertyValue(prop);
  10151. }
  10152. /**
  10153. * Check if an element has the given class name.
  10154. *
  10155. * @function Highcharts.SVGElement#hasClass
  10156. *
  10157. * @param {string} className
  10158. * The class name to check for.
  10159. *
  10160. * @return {boolean}
  10161. * Whether the class name is found.
  10162. */
  10163. hasClass(className) {
  10164. return ('' + this.attr('class'))
  10165. .split(' ')
  10166. .indexOf(className) !== -1;
  10167. }
  10168. /**
  10169. * Hide the element, similar to setting the `visibility` attribute to
  10170. * `hidden`.
  10171. *
  10172. * @function Highcharts.SVGElement#hide
  10173. *
  10174. * @return {Highcharts.SVGElement}
  10175. * Returns the SVGElement for chaining.
  10176. */
  10177. hide() {
  10178. return this.attr({ visibility: 'hidden' });
  10179. }
  10180. /**
  10181. * @private
  10182. */
  10183. htmlGetBBox() {
  10184. return { height: 0, width: 0, x: 0, y: 0 };
  10185. }
  10186. /**
  10187. * Initialize the SVG element. This function only exists to make the
  10188. * initialization process overridable. It should not be called directly.
  10189. *
  10190. * @function Highcharts.SVGElement#init
  10191. *
  10192. * @param {Highcharts.SVGRenderer} renderer
  10193. * The SVGRenderer instance to initialize to.
  10194. *
  10195. * @param {string} nodeName
  10196. * The SVG node name.
  10197. */
  10198. init(renderer, nodeName) {
  10199. /**
  10200. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  10201. * node, but may also represent more nodes.
  10202. *
  10203. * @name Highcharts.SVGElement#element
  10204. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  10205. */
  10206. this.element = nodeName === 'span' ?
  10207. createElement(nodeName) :
  10208. doc.createElementNS(this.SVG_NS, nodeName);
  10209. /**
  10210. * The renderer that the SVGElement belongs to.
  10211. *
  10212. * @name Highcharts.SVGElement#renderer
  10213. * @type {Highcharts.SVGRenderer}
  10214. */
  10215. this.renderer = renderer;
  10216. fireEvent(this, 'afterInit');
  10217. }
  10218. /**
  10219. * Add an event listener. This is a simple setter that replaces the
  10220. * previous event of the same type added by this function, as opposed to
  10221. * the {@link Highcharts#addEvent} function.
  10222. *
  10223. * @sample highcharts/members/element-on/
  10224. * A clickable rectangle
  10225. *
  10226. * @function Highcharts.SVGElement#on
  10227. *
  10228. * @param {string} eventType
  10229. * The event type.
  10230. *
  10231. * @param {Function} handler
  10232. * The handler callback.
  10233. *
  10234. * @return {Highcharts.SVGElement}
  10235. * The SVGElement for chaining.
  10236. */
  10237. on(eventType, handler) {
  10238. const { onEvents } = this;
  10239. if (onEvents[eventType]) {
  10240. // Unbind existing event
  10241. onEvents[eventType]();
  10242. }
  10243. onEvents[eventType] = addEvent(this.element, eventType, handler);
  10244. return this;
  10245. }
  10246. /**
  10247. * @private
  10248. * @function Highcharts.SVGElement#opacitySetter
  10249. * @param {string} value
  10250. * @param {string} key
  10251. * @param {Highcharts.SVGDOMElement} element
  10252. */
  10253. opacitySetter(value, key, element) {
  10254. // Round off to avoid float errors, like tests where opacity lands on
  10255. // 9.86957e-06 instead of 0
  10256. const opacity = Number(Number(value).toFixed(3));
  10257. this.opacity = opacity;
  10258. element.setAttribute(key, opacity);
  10259. }
  10260. /**
  10261. * Remove a class name from the element.
  10262. *
  10263. * @function Highcharts.SVGElement#removeClass
  10264. *
  10265. * @param {string|RegExp} className
  10266. * The class name to remove.
  10267. *
  10268. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  10269. */
  10270. removeClass(className) {
  10271. return this.attr('class', ('' + this.attr('class'))
  10272. .replace(isString(className) ?
  10273. new RegExp(`(^| )${className}( |$)`) : // #12064, #13590
  10274. className, ' ')
  10275. .replace(/ +/g, ' ')
  10276. .trim());
  10277. }
  10278. /**
  10279. *
  10280. * @private
  10281. */
  10282. removeTextOutline() {
  10283. const outline = this.element
  10284. .querySelector('tspan.highcharts-text-outline');
  10285. if (outline) {
  10286. this.safeRemoveChild(outline);
  10287. }
  10288. }
  10289. /**
  10290. * Removes an element from the DOM.
  10291. *
  10292. * @private
  10293. * @function Highcharts.SVGElement#safeRemoveChild
  10294. *
  10295. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  10296. * The DOM node to remove.
  10297. */
  10298. safeRemoveChild(element) {
  10299. const parentNode = element.parentNode;
  10300. if (parentNode) {
  10301. parentNode.removeChild(element);
  10302. }
  10303. }
  10304. /**
  10305. * Set the coordinates needed to draw a consistent radial gradient across
  10306. * a shape regardless of positioning inside the chart. Used on pie slices
  10307. * to make all the slices have the same radial reference point.
  10308. *
  10309. * @function Highcharts.SVGElement#setRadialReference
  10310. *
  10311. * @param {Array<number>} coordinates
  10312. * The center reference. The format is `[centerX, centerY, diameter]` in
  10313. * pixels.
  10314. *
  10315. * @return {Highcharts.SVGElement}
  10316. * Returns the SVGElement for chaining.
  10317. */
  10318. setRadialReference(coordinates) {
  10319. const existingGradient = (this.element.gradient &&
  10320. this.renderer.gradients[this.element.gradient]);
  10321. this.element.radialReference = coordinates;
  10322. // On redrawing objects with an existing gradient, the gradient needs
  10323. // to be repositioned (#3801)
  10324. if (existingGradient && existingGradient.radAttr) {
  10325. existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
  10326. }
  10327. return this;
  10328. }
  10329. /**
  10330. * Set a text path for a `text` or `label` element, allowing the text to
  10331. * flow along a path.
  10332. *
  10333. * In order to unset the path for an existing element, call `setTextPath`
  10334. * with `{ enabled: false }` as the second argument.
  10335. *
  10336. * @sample highcharts/members/renderer-textpath/ Text path demonstrated
  10337. *
  10338. * @function Highcharts.SVGElement#setTextPath
  10339. *
  10340. * @param {Highcharts.SVGElement|undefined} path
  10341. * Path to follow. If undefined, it allows changing options for the
  10342. * existing path.
  10343. *
  10344. * @param {Highcharts.DataLabelsTextPathOptionsObject} textPathOptions
  10345. * Options.
  10346. *
  10347. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  10348. */
  10349. setTextPath(path, textPathOptions) {
  10350. // Defaults
  10351. textPathOptions = merge(true, {
  10352. enabled: true,
  10353. attributes: {
  10354. dy: -5,
  10355. startOffset: '50%',
  10356. textAnchor: 'middle'
  10357. }
  10358. }, textPathOptions);
  10359. const url = this.renderer.url, textWrapper = this.text || this, textPath = textWrapper.textPath, { attributes, enabled } = textPathOptions;
  10360. path = path || (textPath && textPath.path);
  10361. // Remove previously added event
  10362. if (textPath) {
  10363. textPath.undo();
  10364. }
  10365. if (path && enabled) {
  10366. const undo = addEvent(textWrapper, 'afterModifyTree', (e) => {
  10367. if (path && enabled) {
  10368. // Set ID for the path
  10369. let textPathId = path.attr('id');
  10370. if (!textPathId) {
  10371. path.attr('id', textPathId = uniqueKey());
  10372. }
  10373. // Set attributes for the <text>
  10374. const textAttribs = {
  10375. // dx/dy options must by set on <text> (parent), the
  10376. // rest should be set on <textPath>
  10377. x: 0,
  10378. y: 0
  10379. };
  10380. if (defined(attributes.dx)) {
  10381. textAttribs.dx = attributes.dx;
  10382. delete attributes.dx;
  10383. }
  10384. if (defined(attributes.dy)) {
  10385. textAttribs.dy = attributes.dy;
  10386. delete attributes.dy;
  10387. }
  10388. textWrapper.attr(textAttribs);
  10389. // Handle label properties
  10390. this.attr({ transform: '' });
  10391. if (this.box) {
  10392. this.box = this.box.destroy();
  10393. }
  10394. // Wrap the nodes in a textPath
  10395. const children = e.nodes.slice(0);
  10396. e.nodes.length = 0;
  10397. e.nodes[0] = {
  10398. tagName: 'textPath',
  10399. attributes: extend(attributes, {
  10400. 'text-anchor': attributes.textAnchor,
  10401. href: `${url}#${textPathId}`
  10402. }),
  10403. children
  10404. };
  10405. }
  10406. });
  10407. // Set the reference
  10408. textWrapper.textPath = { path, undo };
  10409. }
  10410. else {
  10411. textWrapper.attr({ dx: 0, dy: 0 });
  10412. delete textWrapper.textPath;
  10413. }
  10414. if (this.added) {
  10415. // Rebuild text after added
  10416. textWrapper.textCache = '';
  10417. this.renderer.buildText(textWrapper);
  10418. }
  10419. return this;
  10420. }
  10421. /**
  10422. * Add a shadow to the element. In styled mode, this method is not used,
  10423. * instead use `defs` and filters.
  10424. *
  10425. * @example
  10426. * renderer.rect(10, 100, 100, 100)
  10427. * .attr({ fill: 'red' })
  10428. * .shadow(true);
  10429. *
  10430. * @function Highcharts.SVGElement#shadow
  10431. *
  10432. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
  10433. * shadow options. If `true`, the default options are applied. If
  10434. * `false`, the current shadow will be removed.
  10435. *
  10436. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  10437. */
  10438. shadow(shadowOptions) {
  10439. var _a;
  10440. const { renderer } = this, options = merge(((_a = this.parentGroup) === null || _a === void 0 ? void 0 : _a.rotation) === 90 ? {
  10441. offsetX: -1,
  10442. offsetY: -1
  10443. } : {}, isObject(shadowOptions) ? shadowOptions : {}), id = renderer.shadowDefinition(options);
  10444. return this.attr({
  10445. filter: shadowOptions ?
  10446. `url(${renderer.url}#${id})` :
  10447. 'none'
  10448. });
  10449. }
  10450. /**
  10451. * Show the element after it has been hidden.
  10452. *
  10453. * @function Highcharts.SVGElement#show
  10454. *
  10455. * @param {boolean} [inherit=true]
  10456. * Set the visibility attribute to `inherit` rather than `visible`.
  10457. * The difference is that an element with `visibility="visible"`
  10458. * will be visible even if the parent is hidden.
  10459. *
  10460. * @return {Highcharts.SVGElement}
  10461. * Returns the SVGElement for chaining.
  10462. */
  10463. show(inherit = true) {
  10464. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  10465. }
  10466. /**
  10467. * Set the stroke-width and record it on the SVGElement
  10468. *
  10469. * @private
  10470. * @function Highcharts.SVGElement#strokeSetter
  10471. * @param {number|string|ColorType} value
  10472. * @param {string} key
  10473. * @param {Highcharts.SVGDOMElement} element
  10474. */
  10475. 'stroke-widthSetter'(value, key, element) {
  10476. // Record it for quick access in getter
  10477. this[key] = value;
  10478. element.setAttribute(key, value);
  10479. }
  10480. /**
  10481. * Get the computed stroke width in pixel values. This is used extensively
  10482. * when drawing shapes to ensure the shapes are rendered crisp and
  10483. * positioned correctly relative to each other. Using
  10484. * `shape-rendering: crispEdges` leaves us less control over positioning,
  10485. * for example when we want to stack columns next to each other, or position
  10486. * things pixel-perfectly within the plot box.
  10487. *
  10488. * The common pattern when placing a shape is:
  10489. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  10490. * now receive a stroke width from the style sheet. In classic mode we
  10491. * will add the `stroke-width` attribute.
  10492. * - Read the computed `elem.strokeWidth()`.
  10493. * - Place it based on the stroke width.
  10494. *
  10495. * @function Highcharts.SVGElement#strokeWidth
  10496. *
  10497. * @return {number}
  10498. * The stroke width in pixels. Even if the given stroke widtch (in CSS or by
  10499. * attributes) is based on `em` or other units, the pixel size is returned.
  10500. */
  10501. strokeWidth() {
  10502. // In non-styled mode, read the stroke width as set by .attr
  10503. if (!this.renderer.styledMode) {
  10504. return this['stroke-width'] || 0;
  10505. }
  10506. // In styled mode, read computed stroke width
  10507. const val = this.getStyle('stroke-width');
  10508. let ret = 0, dummy;
  10509. // Read pixel values directly
  10510. if (val.indexOf('px') === val.length - 2) {
  10511. ret = pInt(val);
  10512. // Other values like em, pt etc need to be measured
  10513. }
  10514. else if (val !== '') {
  10515. dummy = doc.createElementNS(SVG_NS, 'rect');
  10516. attr(dummy, {
  10517. width: val,
  10518. 'stroke-width': 0
  10519. });
  10520. this.element.parentNode.appendChild(dummy);
  10521. ret = dummy.getBBox().width;
  10522. dummy.parentNode.removeChild(dummy);
  10523. }
  10524. return ret;
  10525. }
  10526. /**
  10527. * If one of the symbol size affecting parameters are changed,
  10528. * check all the others only once for each call to an element's
  10529. * .attr() method
  10530. *
  10531. * @private
  10532. * @function Highcharts.SVGElement#symbolAttr
  10533. *
  10534. * @param {Highcharts.SVGAttributes} hash
  10535. * The attributes to set.
  10536. */
  10537. symbolAttr(hash) {
  10538. const wrapper = this;
  10539. SVGElement.symbolCustomAttribs.forEach(function (key) {
  10540. wrapper[key] = pick(hash[key], wrapper[key]);
  10541. });
  10542. wrapper.attr({
  10543. d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
  10544. });
  10545. }
  10546. /**
  10547. * @private
  10548. * @function Highcharts.SVGElement#textSetter
  10549. * @param {string} value
  10550. */
  10551. textSetter(value) {
  10552. if (value !== this.textStr) {
  10553. // Delete size caches when the text changes
  10554. // delete this.bBox; // old code in series-label
  10555. delete this.textPxLength;
  10556. this.textStr = value;
  10557. if (this.added) {
  10558. this.renderer.buildText(this);
  10559. }
  10560. }
  10561. }
  10562. /**
  10563. * @private
  10564. * @function Highcharts.SVGElement#titleSetter
  10565. * @param {string} value
  10566. */
  10567. titleSetter(value) {
  10568. const el = this.element;
  10569. const titleNode = el.getElementsByTagName('title')[0] ||
  10570. doc.createElementNS(this.SVG_NS, 'title');
  10571. // Move to first child
  10572. if (el.insertBefore) {
  10573. el.insertBefore(titleNode, el.firstChild);
  10574. }
  10575. else {
  10576. el.appendChild(titleNode);
  10577. }
  10578. // Replace text content and escape markup
  10579. titleNode.textContent =
  10580. // #3276, #3895
  10581. String(pick(value, ''))
  10582. .replace(/<[^>]*>/g, '')
  10583. .replace(/&lt;/g, '<')
  10584. .replace(/&gt;/g, '>');
  10585. }
  10586. /**
  10587. * Bring the element to the front. Alternatively, a new zIndex can be set.
  10588. *
  10589. * @sample highcharts/members/element-tofront/
  10590. * Click an element to bring it to front
  10591. *
  10592. * @function Highcharts.SVGElement#toFront
  10593. *
  10594. * @return {Highcharts.SVGElement}
  10595. * Returns the SVGElement for chaining.
  10596. */
  10597. toFront() {
  10598. const element = this.element;
  10599. element.parentNode.appendChild(element);
  10600. return this;
  10601. }
  10602. /**
  10603. * Move an object and its children by x and y values.
  10604. *
  10605. * @function Highcharts.SVGElement#translate
  10606. *
  10607. * @param {number} x
  10608. * The x value.
  10609. *
  10610. * @param {number} y
  10611. * The y value.
  10612. *
  10613. * @return {Highcharts.SVGElement}
  10614. * Translated element.
  10615. */
  10616. translate(x, y) {
  10617. return this.attr({
  10618. translateX: x,
  10619. translateY: y
  10620. });
  10621. }
  10622. /**
  10623. * Update the transform attribute based on internal properties. Deals with
  10624. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  10625. * attributes and updates the SVG `transform` attribute.
  10626. *
  10627. * @private
  10628. * @function Highcharts.SVGElement#updateTransform
  10629. */
  10630. updateTransform() {
  10631. const { element, matrix, rotation = 0, scaleX, scaleY, translateX = 0, translateY = 0 } = this;
  10632. // Apply translate. Nearly all transformed elements have translation,
  10633. // so instead of checking for translate = 0, do it always (#1767,
  10634. // #1846).
  10635. const transform = ['translate(' + translateX + ',' + translateY + ')'];
  10636. // apply matrix
  10637. if (defined(matrix)) {
  10638. transform.push('matrix(' + matrix.join(',') + ')');
  10639. }
  10640. // Apply rotation
  10641. if (rotation) { // text rotation or inverted chart
  10642. transform.push('rotate(' + rotation + ' ' +
  10643. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  10644. ' ' +
  10645. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')');
  10646. }
  10647. // apply scale
  10648. if (defined(scaleX) || defined(scaleY)) {
  10649. transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
  10650. }
  10651. if (transform.length && !(this.text || this).textPath) {
  10652. element.setAttribute('transform', transform.join(' '));
  10653. }
  10654. }
  10655. /**
  10656. * @private
  10657. * @function Highcharts.SVGElement#visibilitySetter
  10658. *
  10659. * @param {string} value
  10660. *
  10661. * @param {string} key
  10662. *
  10663. * @param {Highcharts.SVGDOMElement} element
  10664. *
  10665. */
  10666. visibilitySetter(value, key, element) {
  10667. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  10668. // attribute instead (#2881, #3909)
  10669. if (value === 'inherit') {
  10670. element.removeAttribute(key);
  10671. }
  10672. else if (this[key] !== value) { // #6747
  10673. element.setAttribute(key, value);
  10674. }
  10675. this[key] = value;
  10676. }
  10677. /**
  10678. * @private
  10679. * @function Highcharts.SVGElement#xGetter
  10680. */
  10681. xGetter(key) {
  10682. if (this.element.nodeName === 'circle') {
  10683. if (key === 'x') {
  10684. key = 'cx';
  10685. }
  10686. else if (key === 'y') {
  10687. key = 'cy';
  10688. }
  10689. }
  10690. return this._defaultGetter(key);
  10691. }
  10692. /**
  10693. * @private
  10694. * @function Highcharts.SVGElement#zIndexSetter
  10695. */
  10696. zIndexSetter(value, key) {
  10697. const renderer = this.renderer, parentGroup = this.parentGroup, parentWrapper = parentGroup || renderer, parentNode = parentWrapper.element || renderer.box, element = this.element, svgParent = parentNode === renderer.box;
  10698. let childNodes, otherElement, otherZIndex, inserted = false, undefinedOtherZIndex, run = this.added, i;
  10699. if (defined(value)) {
  10700. // So we can read it for other elements in the group
  10701. element.setAttribute('data-z-index', value);
  10702. value = +value;
  10703. if (this[key] === value) {
  10704. // Only update when needed (#3865)
  10705. run = false;
  10706. }
  10707. }
  10708. else if (defined(this[key])) {
  10709. element.removeAttribute('data-z-index');
  10710. }
  10711. this[key] = value;
  10712. // Insert according to this and other elements' zIndex. Before .add() is
  10713. // called, nothing is done. Then on add, or by later calls to
  10714. // zIndexSetter, the node is placed on the right place in the DOM.
  10715. if (run) {
  10716. value = this.zIndex;
  10717. if (value && parentGroup) {
  10718. parentGroup.handleZ = true;
  10719. }
  10720. childNodes = parentNode.childNodes;
  10721. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  10722. otherElement = childNodes[i];
  10723. otherZIndex = otherElement.getAttribute('data-z-index');
  10724. undefinedOtherZIndex = !defined(otherZIndex);
  10725. if (otherElement !== element) {
  10726. if (
  10727. // Negative zIndex versus no zIndex:
  10728. // On all levels except the highest. If the parent is
  10729. // <svg>, then we don't want to put items before <desc>
  10730. // or <defs>
  10731. value < 0 &&
  10732. undefinedOtherZIndex &&
  10733. !svgParent &&
  10734. !i) {
  10735. parentNode.insertBefore(element, childNodes[i]);
  10736. inserted = true;
  10737. }
  10738. else if (
  10739. // Insert after the first element with a lower zIndex
  10740. pInt(otherZIndex) <= value ||
  10741. // If negative zIndex, add this before first undefined
  10742. // zIndex element
  10743. (undefinedOtherZIndex &&
  10744. (!defined(value) || value >= 0))) {
  10745. parentNode.insertBefore(element, childNodes[i + 1]);
  10746. inserted = true;
  10747. }
  10748. }
  10749. }
  10750. if (!inserted) {
  10751. parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0]);
  10752. inserted = true;
  10753. }
  10754. }
  10755. return inserted;
  10756. }
  10757. }
  10758. // Custom attributes used for symbols, these should be filtered out when
  10759. // setting SVGElement attributes (#9375).
  10760. SVGElement.symbolCustomAttribs = [
  10761. 'anchorX',
  10762. 'anchorY',
  10763. 'clockwise',
  10764. 'end',
  10765. 'height',
  10766. 'innerR',
  10767. 'r',
  10768. 'start',
  10769. 'width',
  10770. 'x',
  10771. 'y'
  10772. ];
  10773. // Some shared setters and getters
  10774. SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
  10775. SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
  10776. SVGElement.prototype.matrixSetter =
  10777. SVGElement.prototype.rotationOriginXSetter =
  10778. SVGElement.prototype.rotationOriginYSetter =
  10779. SVGElement.prototype.rotationSetter =
  10780. SVGElement.prototype.scaleXSetter =
  10781. SVGElement.prototype.scaleYSetter =
  10782. SVGElement.prototype.translateXSetter =
  10783. SVGElement.prototype.translateYSetter =
  10784. SVGElement.prototype.verticalAlignSetter = function (value, key) {
  10785. this[key] = value;
  10786. this.doTransform = true;
  10787. };
  10788. /* *
  10789. *
  10790. * Default Export
  10791. *
  10792. * */
  10793. /* *
  10794. *
  10795. * API Declarations
  10796. *
  10797. * */
  10798. /**
  10799. * Reference to the global SVGElement class as a workaround for a name conflict
  10800. * in the Highcharts namespace.
  10801. *
  10802. * @global
  10803. * @typedef {global.SVGElement} GlobalSVGElement
  10804. *
  10805. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  10806. */
  10807. /**
  10808. * The horizontal alignment of an element.
  10809. *
  10810. * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
  10811. */
  10812. /**
  10813. * Options to align the element relative to the chart or another box.
  10814. *
  10815. * @interface Highcharts.AlignObject
  10816. */ /**
  10817. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  10818. *
  10819. * @name Highcharts.AlignObject#align
  10820. * @type {Highcharts.AlignValue|undefined}
  10821. *
  10822. * @default left
  10823. */ /**
  10824. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  10825. *
  10826. * @name Highcharts.AlignObject#verticalAlign
  10827. * @type {Highcharts.VerticalAlignValue|undefined}
  10828. *
  10829. * @default top
  10830. */ /**
  10831. * Horizontal pixel offset from alignment.
  10832. *
  10833. * @name Highcharts.AlignObject#x
  10834. * @type {number|undefined}
  10835. *
  10836. * @default 0
  10837. */ /**
  10838. * Vertical pixel offset from alignment.
  10839. *
  10840. * @name Highcharts.AlignObject#y
  10841. * @type {number|undefined}
  10842. *
  10843. * @default 0
  10844. */ /**
  10845. * Use the `transform` attribute with translateX and translateY custom
  10846. * attributes to align this elements rather than `x` and `y` attributes.
  10847. *
  10848. * @name Highcharts.AlignObject#alignByTranslate
  10849. * @type {boolean|undefined}
  10850. *
  10851. * @default false
  10852. */
  10853. /**
  10854. * Bounding box of an element.
  10855. *
  10856. * @interface Highcharts.BBoxObject
  10857. * @extends Highcharts.PositionObject
  10858. */ /**
  10859. * Height of the bounding box.
  10860. *
  10861. * @name Highcharts.BBoxObject#height
  10862. * @type {number}
  10863. */ /**
  10864. * Width of the bounding box.
  10865. *
  10866. * @name Highcharts.BBoxObject#width
  10867. * @type {number}
  10868. */ /**
  10869. * Horizontal position of the bounding box.
  10870. *
  10871. * @name Highcharts.BBoxObject#x
  10872. * @type {number}
  10873. */ /**
  10874. * Vertical position of the bounding box.
  10875. *
  10876. * @name Highcharts.BBoxObject#y
  10877. * @type {number}
  10878. */
  10879. /**
  10880. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  10881. * elements for the most parts correspond to SVG, but some are specific to
  10882. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  10883. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  10884. * attributes containing a hyphen are _not_ camel-cased, they should be
  10885. * quoted to preserve the hyphen.
  10886. *
  10887. * @example
  10888. * {
  10889. * 'stroke': '#ff0000', // basic
  10890. * 'stroke-width': 2, // hyphenated
  10891. * 'rotation': 45 // custom
  10892. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  10893. * }
  10894. *
  10895. * @interface Highcharts.SVGAttributes
  10896. */ /**
  10897. * @name Highcharts.SVGAttributes#[key:string]
  10898. * @type {*}
  10899. */ /**
  10900. * @name Highcharts.SVGAttributes#d
  10901. * @type {string|Highcharts.SVGPathArray|undefined}
  10902. */ /**
  10903. * @name Highcharts.SVGAttributes#dx
  10904. * @type {number|undefined}
  10905. */ /**
  10906. * @name Highcharts.SVGAttributes#dy
  10907. * @type {number|undefined}
  10908. */ /**
  10909. * @name Highcharts.SVGAttributes#fill
  10910. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  10911. */ /**
  10912. * @name Highcharts.SVGAttributes#inverted
  10913. * @type {boolean|undefined}
  10914. */ /**
  10915. * @name Highcharts.SVGAttributes#matrix
  10916. * @type {Array<number>|undefined}
  10917. */ /**
  10918. * @name Highcharts.SVGAttributes#rotation
  10919. * @type {number|undefined}
  10920. */ /**
  10921. * @name Highcharts.SVGAttributes#rotationOriginX
  10922. * @type {number|undefined}
  10923. */ /**
  10924. * @name Highcharts.SVGAttributes#rotationOriginY
  10925. * @type {number|undefined}
  10926. */ /**
  10927. * @name Highcharts.SVGAttributes#scaleX
  10928. * @type {number|undefined}
  10929. */ /**
  10930. * @name Highcharts.SVGAttributes#scaleY
  10931. * @type {number|undefined}
  10932. */ /**
  10933. * @name Highcharts.SVGAttributes#stroke
  10934. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  10935. */ /**
  10936. * @name Highcharts.SVGAttributes#style
  10937. * @type {string|Highcharts.CSSObject|undefined}
  10938. */ /**
  10939. * @name Highcharts.SVGAttributes#translateX
  10940. * @type {number|undefined}
  10941. */ /**
  10942. * @name Highcharts.SVGAttributes#translateY
  10943. * @type {number|undefined}
  10944. */ /**
  10945. * @name Highcharts.SVGAttributes#zIndex
  10946. * @type {number|undefined}
  10947. */
  10948. /**
  10949. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  10950. * global scope.
  10951. *
  10952. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  10953. *
  10954. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  10955. */
  10956. /**
  10957. * The vertical alignment of an element.
  10958. *
  10959. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
  10960. */
  10961. ''; // keeps doclets above in JS file
  10962. return SVGElement;
  10963. });
  10964. _registerModule(_modules, 'Core/Renderer/RendererRegistry.js', [_modules['Core/Globals.js']], function (H) {
  10965. /* *
  10966. *
  10967. * (c) 2010-2021 Torstein Honsi
  10968. *
  10969. * License: www.highcharts.com/license
  10970. *
  10971. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10972. *
  10973. * */
  10974. /* *
  10975. *
  10976. * Namespace
  10977. *
  10978. * */
  10979. var RendererRegistry;
  10980. (function (RendererRegistry) {
  10981. /* *
  10982. *
  10983. * Constants
  10984. *
  10985. * */
  10986. RendererRegistry.rendererTypes = {};
  10987. /* *
  10988. *
  10989. * Variables
  10990. *
  10991. * */
  10992. let defaultRenderer;
  10993. /* *
  10994. *
  10995. * Functions
  10996. *
  10997. * */
  10998. /**
  10999. * Gets a registered renderer class. If no renderer type is provided or the
  11000. * requested renderer was not founded, the default renderer is returned.
  11001. *
  11002. * @param {string} [rendererType]
  11003. * Renderer type or the default renderer.
  11004. *
  11005. * @return {Highcharts.Class<Highcharts.SVGRenderer>}
  11006. * Returns the requested renderer class or the default renderer class.
  11007. */
  11008. function getRendererType(rendererType = defaultRenderer) {
  11009. return (RendererRegistry.rendererTypes[rendererType] || RendererRegistry.rendererTypes[defaultRenderer]);
  11010. }
  11011. RendererRegistry.getRendererType = getRendererType;
  11012. /**
  11013. * Register a renderer class.
  11014. *
  11015. * @param {string} rendererType
  11016. * Renderer type to register.
  11017. *
  11018. * @param {Highcharts.Class<Highcharts.SVGRenderer>} rendererClass
  11019. * Returns the requested renderer class or the default renderer class.
  11020. *
  11021. * @param {boolean} setAsDefault
  11022. * Sets the renderer class as the default renderer.
  11023. */
  11024. function registerRendererType(rendererType, rendererClass, setAsDefault) {
  11025. RendererRegistry.rendererTypes[rendererType] = rendererClass;
  11026. if (!defaultRenderer || setAsDefault) {
  11027. defaultRenderer = rendererType;
  11028. H.Renderer = rendererClass; // compatibility
  11029. }
  11030. }
  11031. RendererRegistry.registerRendererType = registerRendererType;
  11032. })(RendererRegistry || (RendererRegistry = {}));
  11033. /* *
  11034. *
  11035. * Default Export
  11036. *
  11037. * */
  11038. return RendererRegistry;
  11039. });
  11040. _registerModule(_modules, 'Core/Renderer/SVG/SVGLabel.js', [_modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (SVGElement, U) {
  11041. /* *
  11042. *
  11043. * (c) 2010-2021 Torstein Honsi
  11044. *
  11045. * License: www.highcharts.com/license
  11046. *
  11047. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11048. *
  11049. * */
  11050. const { defined, extend, isNumber, merge, pick, removeEvent } = U;
  11051. /* *
  11052. *
  11053. * Class
  11054. *
  11055. * */
  11056. /**
  11057. * SVG label to render text.
  11058. * @private
  11059. * @class
  11060. * @name Highcharts.SVGLabel
  11061. * @augments Highcharts.SVGElement
  11062. */
  11063. class SVGLabel extends SVGElement {
  11064. /* *
  11065. *
  11066. * Constructor
  11067. *
  11068. * */
  11069. constructor(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  11070. super();
  11071. this.paddingLeftSetter = this.paddingSetter;
  11072. this.paddingRightSetter = this.paddingSetter;
  11073. this.init(renderer, 'g');
  11074. this.textStr = str;
  11075. this.x = x;
  11076. this.y = y;
  11077. this.anchorX = anchorX;
  11078. this.anchorY = anchorY;
  11079. this.baseline = baseline;
  11080. this.className = className;
  11081. this.addClass(className === 'button' ?
  11082. 'highcharts-no-tooltip' :
  11083. 'highcharts-label');
  11084. if (className) {
  11085. this.addClass('highcharts-' + className);
  11086. }
  11087. // Create the text element. An undefined text content prevents redundant
  11088. // box calculation (#16121)
  11089. this.text = renderer.text(void 0, 0, 0, useHTML).attr({ zIndex: 1 });
  11090. // Validate the shape argument
  11091. let hasBGImage;
  11092. if (typeof shape === 'string') {
  11093. hasBGImage = /^url\((.*?)\)$/.test(shape);
  11094. if (hasBGImage || this.renderer.symbols[shape]) {
  11095. this.symbolKey = shape;
  11096. }
  11097. }
  11098. this.bBox = SVGLabel.emptyBBox;
  11099. this.padding = 3;
  11100. this.baselineOffset = 0;
  11101. this.needsBox = renderer.styledMode || hasBGImage;
  11102. this.deferredAttr = {};
  11103. this.alignFactor = 0;
  11104. }
  11105. /* *
  11106. *
  11107. * Functions
  11108. *
  11109. * */
  11110. alignSetter(value) {
  11111. const alignFactor = ({
  11112. left: 0,
  11113. center: 0.5,
  11114. right: 1
  11115. })[value];
  11116. if (alignFactor !== this.alignFactor) {
  11117. this.alignFactor = alignFactor;
  11118. // Bounding box exists, means we're dynamically changing
  11119. if (this.bBox && isNumber(this.xSetting)) {
  11120. this.attr({ x: this.xSetting }); // #5134
  11121. }
  11122. }
  11123. }
  11124. anchorXSetter(value, key) {
  11125. this.anchorX = value;
  11126. this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
  11127. }
  11128. anchorYSetter(value, key) {
  11129. this.anchorY = value;
  11130. this.boxAttr(key, value - this.ySetting);
  11131. }
  11132. /*
  11133. * Set a box attribute, or defer it if the box is not yet created
  11134. */
  11135. boxAttr(key, value) {
  11136. if (this.box) {
  11137. this.box.attr(key, value);
  11138. }
  11139. else {
  11140. this.deferredAttr[key] = value;
  11141. }
  11142. }
  11143. /*
  11144. * Pick up some properties and apply them to the text instead of the
  11145. * wrapper.
  11146. */
  11147. css(styles) {
  11148. if (styles) {
  11149. const textStyles = {};
  11150. // Create a copy to avoid altering the original object
  11151. // (#537)
  11152. styles = merge(styles);
  11153. SVGLabel.textProps.forEach((prop) => {
  11154. if (typeof styles[prop] !== 'undefined') {
  11155. textStyles[prop] = styles[prop];
  11156. delete styles[prop];
  11157. }
  11158. });
  11159. this.text.css(textStyles);
  11160. // Update existing text, box (#9400, #12163, #18212)
  11161. if ('fontSize' in textStyles || 'fontWeight' in textStyles) {
  11162. this.updateTextPadding();
  11163. }
  11164. else if ('width' in textStyles || 'textOverflow' in textStyles) {
  11165. this.updateBoxSize();
  11166. }
  11167. }
  11168. return SVGElement.prototype.css.call(this, styles);
  11169. }
  11170. /*
  11171. * Destroy and release memory.
  11172. */
  11173. destroy() {
  11174. // Added by button implementation
  11175. removeEvent(this.element, 'mouseenter');
  11176. removeEvent(this.element, 'mouseleave');
  11177. if (this.text) {
  11178. this.text.destroy();
  11179. }
  11180. if (this.box) {
  11181. this.box = this.box.destroy();
  11182. }
  11183. // Call base implementation to destroy the rest
  11184. SVGElement.prototype.destroy.call(this);
  11185. return void 0;
  11186. }
  11187. fillSetter(value, key) {
  11188. if (value) {
  11189. this.needsBox = true;
  11190. }
  11191. // for animation getter (#6776)
  11192. this.fill = value;
  11193. this.boxAttr(key, value);
  11194. }
  11195. /*
  11196. * Return the bounding box of the box, not the group.
  11197. */
  11198. getBBox() {
  11199. // If we have a text string and the DOM bBox was 0, it typically means
  11200. // that the label was first rendered hidden, so we need to update the
  11201. // bBox (#15246)
  11202. if (this.textStr && this.bBox.width === 0 && this.bBox.height === 0) {
  11203. this.updateBoxSize();
  11204. }
  11205. const padding = this.padding;
  11206. const paddingLeft = pick(this.paddingLeft, padding);
  11207. return {
  11208. width: this.width,
  11209. height: this.height,
  11210. x: this.bBox.x - paddingLeft,
  11211. y: this.bBox.y - padding
  11212. };
  11213. }
  11214. getCrispAdjust() {
  11215. return this.renderer.styledMode && this.box ?
  11216. this.box.strokeWidth() % 2 / 2 :
  11217. (this['stroke-width'] ? parseInt(this['stroke-width'], 10) : 0) % 2 / 2;
  11218. }
  11219. heightSetter(value) {
  11220. this.heightSetting = value;
  11221. }
  11222. /*
  11223. * After the text element is added, get the desired size of the border
  11224. * box and add it before the text in the DOM.
  11225. */
  11226. onAdd() {
  11227. this.text.add(this);
  11228. this.attr({
  11229. // Alignment is available now (#3295, 0 not rendered if given
  11230. // as a value)
  11231. text: pick(this.textStr, ''),
  11232. x: this.x || 0,
  11233. y: this.y || 0
  11234. });
  11235. if (this.box && defined(this.anchorX)) {
  11236. this.attr({
  11237. anchorX: this.anchorX,
  11238. anchorY: this.anchorY
  11239. });
  11240. }
  11241. }
  11242. paddingSetter(value, key) {
  11243. if (!isNumber(value)) {
  11244. this[key] = void 0;
  11245. }
  11246. else if (value !== this[key]) {
  11247. this[key] = value;
  11248. this.updateTextPadding();
  11249. }
  11250. }
  11251. rSetter(value, key) {
  11252. this.boxAttr(key, value);
  11253. }
  11254. strokeSetter(value, key) {
  11255. // for animation getter (#6776)
  11256. this.stroke = value;
  11257. this.boxAttr(key, value);
  11258. }
  11259. 'stroke-widthSetter'(value, key) {
  11260. if (value) {
  11261. this.needsBox = true;
  11262. }
  11263. this['stroke-width'] = value;
  11264. this.boxAttr(key, value);
  11265. }
  11266. 'text-alignSetter'(value) {
  11267. this.textAlign = value;
  11268. }
  11269. textSetter(text) {
  11270. if (typeof text !== 'undefined') {
  11271. // Must use .attr to ensure transforms are done (#10009)
  11272. this.text.attr({ text });
  11273. }
  11274. this.updateTextPadding();
  11275. }
  11276. /*
  11277. * This function runs after the label is added to the DOM (when the bounding
  11278. * box is available), and after the text of the label is updated to detect
  11279. * the new bounding box and reflect it in the border box.
  11280. */
  11281. updateBoxSize() {
  11282. const text = this.text, attribs = {}, padding = this.padding,
  11283. // #12165 error when width is null (auto)
  11284. // #12163 when fontweight: bold, recalculate bBox withot cache
  11285. // #3295 && 3514 box failure when string equals 0
  11286. bBox = this.bBox = (((!isNumber(this.widthSetting) ||
  11287. !isNumber(this.heightSetting) ||
  11288. this.textAlign) && defined(text.textStr)) ?
  11289. text.getBBox() :
  11290. SVGLabel.emptyBBox);
  11291. let crispAdjust;
  11292. this.width = this.getPaddedWidth();
  11293. this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
  11294. const metrics = this.renderer.fontMetrics(text);
  11295. // Update the label-scoped y offset. Math.min because of inline
  11296. // style (#9400)
  11297. this.baselineOffset = padding + Math.min(
  11298. // When applicable, use the font size of the first line (#15707)
  11299. (this.text.firstLineMetrics || metrics).b,
  11300. // When the height is 0, there is no bBox, so go with the font
  11301. // metrics. Highmaps CSS demos.
  11302. bBox.height || Infinity);
  11303. // #15491: Vertical centering
  11304. if (this.heightSetting) {
  11305. this.baselineOffset += (this.heightSetting - metrics.h) / 2;
  11306. }
  11307. if (this.needsBox && !text.textPath) {
  11308. // Create the border box if it is not already present
  11309. if (!this.box) {
  11310. // Symbol definition exists (#5324)
  11311. const box = this.box = this.symbolKey ?
  11312. this.renderer.symbol(this.symbolKey) :
  11313. this.renderer.rect();
  11314. box.addClass(// Don't use label className for buttons
  11315. (this.className === 'button' ?
  11316. '' : 'highcharts-label-box') +
  11317. (this.className ?
  11318. ' highcharts-' + this.className + '-box' : ''));
  11319. box.add(this);
  11320. }
  11321. crispAdjust = this.getCrispAdjust();
  11322. attribs.x = crispAdjust;
  11323. attribs.y = ((this.baseline ? -this.baselineOffset : 0) + crispAdjust);
  11324. // Apply the box attributes
  11325. attribs.width = Math.round(this.width);
  11326. attribs.height = Math.round(this.height);
  11327. this.box.attr(extend(attribs, this.deferredAttr));
  11328. this.deferredAttr = {};
  11329. }
  11330. }
  11331. /*
  11332. * This function runs after setting text or padding, but only if padding
  11333. * is changed.
  11334. */
  11335. updateTextPadding() {
  11336. const text = this.text;
  11337. if (!text.textPath) {
  11338. this.updateBoxSize();
  11339. // Determine y based on the baseline
  11340. const textY = this.baseline ? 0 : this.baselineOffset;
  11341. let textX = pick(this.paddingLeft, this.padding);
  11342. // compensate for alignment
  11343. if (defined(this.widthSetting) &&
  11344. this.bBox &&
  11345. (this.textAlign === 'center' || this.textAlign === 'right')) {
  11346. textX += { center: 0.5, right: 1 }[this.textAlign] * (this.widthSetting - this.bBox.width);
  11347. }
  11348. // update if anything changed
  11349. if (textX !== text.x || textY !== text.y) {
  11350. text.attr('x', textX);
  11351. // #8159 - prevent misplaced data labels in treemap
  11352. // (useHTML: true)
  11353. if (text.hasBoxWidthChanged) {
  11354. this.bBox = text.getBBox(true);
  11355. }
  11356. if (typeof textY !== 'undefined') {
  11357. text.attr('y', textY);
  11358. }
  11359. }
  11360. // record current values
  11361. text.x = textX;
  11362. text.y = textY;
  11363. }
  11364. }
  11365. widthSetter(value) {
  11366. // width:auto => null
  11367. this.widthSetting = isNumber(value) ? value : void 0;
  11368. }
  11369. getPaddedWidth() {
  11370. const padding = this.padding;
  11371. const paddingLeft = pick(this.paddingLeft, padding);
  11372. const paddingRight = pick(this.paddingRight, padding);
  11373. return ((this.widthSetting || this.bBox.width || 0) +
  11374. paddingLeft +
  11375. paddingRight);
  11376. }
  11377. xSetter(value) {
  11378. this.x = value; // for animation getter
  11379. if (this.alignFactor) {
  11380. value -= this.alignFactor * this.getPaddedWidth();
  11381. // Force animation even when setting to the same value (#7898)
  11382. this['forceAnimate:x'] = true;
  11383. }
  11384. this.xSetting = Math.round(value);
  11385. this.attr('translateX', this.xSetting);
  11386. }
  11387. ySetter(value) {
  11388. this.ySetting = this.y = Math.round(value);
  11389. this.attr('translateY', this.ySetting);
  11390. }
  11391. }
  11392. /* *
  11393. *
  11394. * Static Properties
  11395. *
  11396. * */
  11397. SVGLabel.emptyBBox = {
  11398. width: 0,
  11399. height: 0,
  11400. x: 0,
  11401. y: 0
  11402. };
  11403. /**
  11404. * For labels, these CSS properties are applied to the `text` node directly.
  11405. *
  11406. * @private
  11407. * @name Highcharts.SVGLabel#textProps
  11408. * @type {Array<string>}
  11409. */
  11410. SVGLabel.textProps = [
  11411. 'color', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
  11412. 'fontWeight', 'lineHeight', 'textAlign', 'textDecoration',
  11413. 'textOutline', 'textOverflow', 'whiteSpace', 'width'
  11414. ];
  11415. /* *
  11416. *
  11417. * Default Export
  11418. *
  11419. * */
  11420. return SVGLabel;
  11421. });
  11422. _registerModule(_modules, 'Core/Renderer/SVG/Symbols.js', [_modules['Core/Utilities.js']], function (U) {
  11423. /* *
  11424. *
  11425. * (c) 2010-2021 Torstein Honsi
  11426. *
  11427. * License: www.highcharts.com/license
  11428. *
  11429. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11430. *
  11431. * */
  11432. const { defined, isNumber, pick } = U;
  11433. /* *
  11434. *
  11435. * Functions
  11436. *
  11437. * */
  11438. /* eslint-disable require-jsdoc, valid-jsdoc */
  11439. function arc(cx, cy, w, h, options) {
  11440. const arc = [];
  11441. if (options) {
  11442. const start = options.start || 0, rx = pick(options.r, w), ry = pick(options.r, h || w), proximity = 0.001, fullCircle = (Math.abs((options.end || 0) - start - 2 * Math.PI) <
  11443. proximity),
  11444. // Substract a small number to prevent cos and sin of start
  11445. // and end from becoming equal on 360 arcs (related: #1561)
  11446. end = (options.end || 0) - proximity, innerRadius = options.innerR, open = pick(options.open, fullCircle), cosStart = Math.cos(start), sinStart = Math.sin(start), cosEnd = Math.cos(end), sinEnd = Math.sin(end),
  11447. // Proximity takes care of rounding errors around PI (#6971)
  11448. longArc = pick(options.longArc, end - start - Math.PI < proximity ? 0 : 1);
  11449. let arcSegment = [
  11450. 'A',
  11451. rx,
  11452. ry,
  11453. 0,
  11454. longArc,
  11455. pick(options.clockwise, 1),
  11456. cx + rx * cosEnd,
  11457. cy + ry * sinEnd
  11458. ];
  11459. arcSegment.params = { start, end, cx, cy }; // Memo for border radius
  11460. arc.push([
  11461. 'M',
  11462. cx + rx * cosStart,
  11463. cy + ry * sinStart
  11464. ], arcSegment);
  11465. if (defined(innerRadius)) {
  11466. arcSegment = [
  11467. 'A',
  11468. innerRadius,
  11469. innerRadius,
  11470. 0,
  11471. longArc,
  11472. // Clockwise - opposite to the outer arc clockwise
  11473. defined(options.clockwise) ? 1 - options.clockwise : 0,
  11474. cx + innerRadius * cosStart,
  11475. cy + innerRadius * sinStart
  11476. ];
  11477. // Memo for border radius
  11478. arcSegment.params = {
  11479. start: end,
  11480. end: start,
  11481. cx,
  11482. cy
  11483. };
  11484. arc.push(open ?
  11485. [
  11486. 'M',
  11487. cx + innerRadius * cosEnd,
  11488. cy + innerRadius * sinEnd
  11489. ] : [
  11490. 'L',
  11491. cx + innerRadius * cosEnd,
  11492. cy + innerRadius * sinEnd
  11493. ], arcSegment);
  11494. }
  11495. if (!open) {
  11496. arc.push(['Z']);
  11497. }
  11498. }
  11499. return arc;
  11500. }
  11501. /**
  11502. * Callout shape used for default tooltips.
  11503. */
  11504. function callout(x, y, w, h, options) {
  11505. const arrowLength = 6, halfDistance = 6, r = Math.min((options && options.r) || 0, w, h), safeDistance = r + halfDistance, anchorX = options && options.anchorX, anchorY = options && options.anchorY || 0;
  11506. const path = roundedRect(x, y, w, h, { r });
  11507. if (!isNumber(anchorX)) {
  11508. return path;
  11509. }
  11510. // Anchor on right side
  11511. if (x + anchorX >= w) {
  11512. // Chevron
  11513. if (anchorY > y + safeDistance &&
  11514. anchorY < y + h - safeDistance) {
  11515. path.splice(3, 1, ['L', x + w, anchorY - halfDistance], ['L', x + w + arrowLength, anchorY], ['L', x + w, anchorY + halfDistance], ['L', x + w, y + h - r]);
  11516. // Simple connector
  11517. }
  11518. else {
  11519. path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]);
  11520. }
  11521. // Anchor on left side
  11522. }
  11523. else if (x + anchorX <= 0) {
  11524. // Chevron
  11525. if (anchorY > y + safeDistance &&
  11526. anchorY < y + h - safeDistance) {
  11527. path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]);
  11528. // Simple connector
  11529. }
  11530. else {
  11531. path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]);
  11532. }
  11533. }
  11534. else if ( // replace bottom
  11535. anchorY &&
  11536. anchorY > h &&
  11537. anchorX > x + safeDistance &&
  11538. anchorX < x + w - safeDistance) {
  11539. path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]);
  11540. }
  11541. else if ( // replace top
  11542. anchorY &&
  11543. anchorY < 0 &&
  11544. anchorX > x + safeDistance &&
  11545. anchorX < x + w - safeDistance) {
  11546. path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]);
  11547. }
  11548. return path;
  11549. }
  11550. function circle(x, y, w, h) {
  11551. // Return a full arc
  11552. return arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  11553. start: Math.PI * 0.5,
  11554. end: Math.PI * 2.5,
  11555. open: false
  11556. });
  11557. }
  11558. function diamond(x, y, w, h) {
  11559. return [
  11560. ['M', x + w / 2, y],
  11561. ['L', x + w, y + h / 2],
  11562. ['L', x + w / 2, y + h],
  11563. ['L', x, y + h / 2],
  11564. ['Z']
  11565. ];
  11566. }
  11567. // #15291
  11568. function rect(x, y, w, h, options) {
  11569. if (options && options.r) {
  11570. return roundedRect(x, y, w, h, options);
  11571. }
  11572. return [
  11573. ['M', x, y],
  11574. ['L', x + w, y],
  11575. ['L', x + w, y + h],
  11576. ['L', x, y + h],
  11577. ['Z']
  11578. ];
  11579. }
  11580. function roundedRect(x, y, w, h, options) {
  11581. const r = (options === null || options === void 0 ? void 0 : options.r) || 0;
  11582. return [
  11583. ['M', x + r, y],
  11584. ['L', x + w - r, y],
  11585. ['A', r, r, 0, 0, 1, x + w, y + r],
  11586. ['L', x + w, y + h - r],
  11587. ['A', r, r, 0, 0, 1, x + w - r, y + h],
  11588. ['L', x + r, y + h],
  11589. ['A', r, r, 0, 0, 1, x, y + h - r],
  11590. ['L', x, y + r],
  11591. ['A', r, r, 0, 0, 1, x + r, y],
  11592. ['Z'] // top-left corner
  11593. ];
  11594. }
  11595. function triangle(x, y, w, h) {
  11596. return [
  11597. ['M', x + w / 2, y],
  11598. ['L', x + w, y + h],
  11599. ['L', x, y + h],
  11600. ['Z']
  11601. ];
  11602. }
  11603. function triangleDown(x, y, w, h) {
  11604. return [
  11605. ['M', x, y],
  11606. ['L', x + w, y],
  11607. ['L', x + w / 2, y + h],
  11608. ['Z']
  11609. ];
  11610. }
  11611. const Symbols = {
  11612. arc,
  11613. callout,
  11614. circle,
  11615. diamond,
  11616. rect,
  11617. roundedRect,
  11618. square: rect,
  11619. triangle,
  11620. 'triangle-down': triangleDown
  11621. };
  11622. /* *
  11623. *
  11624. * Default Export
  11625. *
  11626. * */
  11627. return Symbols;
  11628. });
  11629. _registerModule(_modules, 'Core/Renderer/SVG/TextBuilder.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (AST, H, U) {
  11630. /* *
  11631. *
  11632. * (c) 2010-2020 Torstein Honsi
  11633. *
  11634. * License: www.highcharts.com/license
  11635. *
  11636. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11637. *
  11638. * */
  11639. const { doc, SVG_NS, win } = H;
  11640. const { attr, extend, fireEvent, isString, objectEach, pick } = U;
  11641. /* *
  11642. *
  11643. * Class
  11644. *
  11645. * */
  11646. /**
  11647. * SVG Text Builder
  11648. * @private
  11649. * @class
  11650. * @name Highcharts.TextBuilder
  11651. */
  11652. class TextBuilder {
  11653. constructor(svgElement) {
  11654. const textStyles = svgElement.styles;
  11655. this.renderer = svgElement.renderer;
  11656. this.svgElement = svgElement;
  11657. this.width = svgElement.textWidth;
  11658. this.textLineHeight = textStyles && textStyles.lineHeight;
  11659. this.textOutline = textStyles && textStyles.textOutline;
  11660. this.ellipsis = Boolean(textStyles && textStyles.textOverflow === 'ellipsis');
  11661. this.noWrap = Boolean(textStyles && textStyles.whiteSpace === 'nowrap');
  11662. }
  11663. /**
  11664. * Build an SVG representation of the pseudo HTML given in the object's
  11665. * svgElement.
  11666. *
  11667. * @private
  11668. *
  11669. * @return {void}.
  11670. */
  11671. buildSVG() {
  11672. const wrapper = this.svgElement, textNode = wrapper.element, renderer = wrapper.renderer, textStr = pick(wrapper.textStr, '').toString(), hasMarkup = textStr.indexOf('<') !== -1, childNodes = textNode.childNodes, tempParent = !wrapper.added && renderer.box, regexMatchBreaks = /<br.*?>/g,
  11673. // The buildText code is quite heavy, so if we're not changing
  11674. // something that affects the text, skip it (#6113).
  11675. textCache = [
  11676. textStr,
  11677. this.ellipsis,
  11678. this.noWrap,
  11679. this.textLineHeight,
  11680. this.textOutline,
  11681. wrapper.getStyle('font-size'),
  11682. this.width
  11683. ].join(',');
  11684. if (textCache === wrapper.textCache) {
  11685. return;
  11686. }
  11687. wrapper.textCache = textCache;
  11688. delete wrapper.actualWidth;
  11689. // Remove old text
  11690. for (let i = childNodes.length; i--;) {
  11691. textNode.removeChild(childNodes[i]);
  11692. }
  11693. // Simple strings, add text directly and return
  11694. if (!hasMarkup &&
  11695. !this.ellipsis &&
  11696. !this.width &&
  11697. !wrapper.textPath &&
  11698. (textStr.indexOf(' ') === -1 ||
  11699. (this.noWrap && !regexMatchBreaks.test(textStr)))) {
  11700. textNode.appendChild(doc.createTextNode(this.unescapeEntities(textStr)));
  11701. // Complex strings, add more logic
  11702. }
  11703. else if (textStr !== '') {
  11704. if (tempParent) {
  11705. // attach it to the DOM to read offset width and font size
  11706. tempParent.appendChild(textNode);
  11707. }
  11708. // Step 1. Parse the markup safely and directly into a tree
  11709. // structure.
  11710. const ast = new AST(textStr);
  11711. // Step 2. Do as many as we can of the modifications to the tree
  11712. // structure before it is added to the DOM
  11713. this.modifyTree(ast.nodes);
  11714. ast.addToDOM(textNode);
  11715. // Step 3. Some modifications can't be done until the structure is
  11716. // in the DOM, because we need to read computed metrics.
  11717. this.modifyDOM();
  11718. // Add title if an ellipsis was added
  11719. if (this.ellipsis &&
  11720. (textNode.textContent || '').indexOf('\u2026') !== -1) {
  11721. wrapper.attr('title', this.unescapeEntities(wrapper.textStr || '', ['&lt;', '&gt;']) // #7179
  11722. );
  11723. }
  11724. if (tempParent) {
  11725. tempParent.removeChild(textNode);
  11726. }
  11727. }
  11728. // Apply the text outline
  11729. if (isString(this.textOutline) && wrapper.applyTextOutline) {
  11730. wrapper.applyTextOutline(this.textOutline);
  11731. }
  11732. }
  11733. /**
  11734. * Modify the DOM of the generated SVG structure. This function only does
  11735. * operations that cannot be done until the elements are attached to the
  11736. * DOM, like doing layout based on rendered metrics of the added elements.
  11737. *
  11738. * @private
  11739. *
  11740. */
  11741. modifyDOM() {
  11742. const wrapper = this.svgElement;
  11743. const x = attr(wrapper.element, 'x');
  11744. wrapper.firstLineMetrics = void 0;
  11745. // Remove empty tspans (including breaks) from the beginning because
  11746. // SVG's getBBox doesn't count empty lines. The use case is tooltip
  11747. // where the header is empty. By doing this in the DOM rather than in
  11748. // the AST, we can inspect the textContent directly and don't have to
  11749. // recurse down to look for valid content.
  11750. let firstChild;
  11751. while ((firstChild = wrapper.element.firstChild)) {
  11752. if (/^[\s\u200B]*$/.test(firstChild.textContent || ' ')) {
  11753. wrapper.element.removeChild(firstChild);
  11754. }
  11755. else {
  11756. break;
  11757. }
  11758. }
  11759. // Modify hard line breaks by applying the rendered line height
  11760. [].forEach.call(wrapper.element.querySelectorAll('tspan.highcharts-br'), (br, i) => {
  11761. if (br.nextSibling && br.previousSibling) { // #5261
  11762. if (i === 0 && br.previousSibling.nodeType === 1) {
  11763. wrapper.firstLineMetrics = wrapper.renderer
  11764. .fontMetrics(br.previousSibling);
  11765. }
  11766. attr(br, {
  11767. // Since the break is inserted in front of the next
  11768. // line, we need to use the next sibling for the line
  11769. // height
  11770. dy: this.getLineHeight(br.nextSibling),
  11771. x
  11772. });
  11773. }
  11774. });
  11775. // Constrain the line width, either by ellipsis or wrapping
  11776. const width = this.width || 0;
  11777. if (!width) {
  11778. return;
  11779. }
  11780. // Insert soft line breaks into each text node
  11781. const modifyTextNode = (textNode, parentElement) => {
  11782. const text = textNode.textContent || '';
  11783. const words = text
  11784. .replace(/([^\^])-/g, '$1- ') // Split on hyphens
  11785. // .trim()
  11786. .split(' '); // #1273
  11787. const hasWhiteSpace = !this.noWrap && (words.length > 1 || wrapper.element.childNodes.length > 1);
  11788. const dy = this.getLineHeight(parentElement);
  11789. let lineNo = 0;
  11790. let startAt = wrapper.actualWidth;
  11791. if (this.ellipsis) {
  11792. if (text) {
  11793. this.truncate(textNode, text, void 0, 0,
  11794. // Target width
  11795. Math.max(0,
  11796. // Substract the font face to make room for the
  11797. // ellipsis itself
  11798. width - 0.8 * dy),
  11799. // Build the text to test for
  11800. (text, currentIndex) => text.substring(0, currentIndex) + '\u2026');
  11801. }
  11802. }
  11803. else if (hasWhiteSpace) {
  11804. const lines = [];
  11805. // Remove preceding siblings in order to make the text length
  11806. // calculation correct in the truncate function
  11807. const precedingSiblings = [];
  11808. while (parentElement.firstChild &&
  11809. parentElement.firstChild !== textNode) {
  11810. precedingSiblings.push(parentElement.firstChild);
  11811. parentElement.removeChild(parentElement.firstChild);
  11812. }
  11813. while (words.length) {
  11814. // Apply the previous line
  11815. if (words.length && !this.noWrap && lineNo > 0) {
  11816. lines.push(textNode.textContent || '');
  11817. textNode.textContent = words.join(' ')
  11818. .replace(/- /g, '-');
  11819. }
  11820. // For each line, truncate the remaining
  11821. // words into the line length.
  11822. this.truncate(textNode, void 0, words, lineNo === 0 ? (startAt || 0) : 0, width,
  11823. // Build the text to test for
  11824. (t, currentIndex) => words
  11825. .slice(0, currentIndex)
  11826. .join(' ')
  11827. .replace(/- /g, '-'));
  11828. startAt = wrapper.actualWidth;
  11829. lineNo++;
  11830. }
  11831. // Reinsert the preceding child nodes
  11832. precedingSiblings.forEach((childNode) => {
  11833. parentElement.insertBefore(childNode, textNode);
  11834. });
  11835. // Insert the previous lines before the original text node
  11836. lines.forEach((line) => {
  11837. // Insert the line
  11838. parentElement.insertBefore(doc.createTextNode(line), textNode);
  11839. // Insert a break
  11840. const br = doc.createElementNS(SVG_NS, 'tspan');
  11841. br.textContent = '\u200B'; // zero-width space
  11842. attr(br, { dy, x });
  11843. parentElement.insertBefore(br, textNode);
  11844. });
  11845. }
  11846. };
  11847. // Recurse down the DOM tree and handle line breaks for each text node
  11848. const modifyChildren = ((node) => {
  11849. const childNodes = [].slice.call(node.childNodes);
  11850. childNodes.forEach((childNode) => {
  11851. if (childNode.nodeType === win.Node.TEXT_NODE) {
  11852. modifyTextNode(childNode, node);
  11853. }
  11854. else {
  11855. // Reset word-wrap width readings after hard breaks
  11856. if (childNode.className.baseVal
  11857. .indexOf('highcharts-br') !== -1) {
  11858. wrapper.actualWidth = 0;
  11859. }
  11860. // Recurse down to child node
  11861. modifyChildren(childNode);
  11862. }
  11863. });
  11864. });
  11865. modifyChildren(wrapper.element);
  11866. }
  11867. /**
  11868. * Get the rendered line height of a <text>, <tspan> or pure text node.
  11869. *
  11870. * @param {DOMElementType|Text} node The node to check for
  11871. *
  11872. * @return {number} The rendered line height
  11873. */
  11874. getLineHeight(node) {
  11875. // If the node is a text node, use its parent
  11876. const element = (node.nodeType === win.Node.TEXT_NODE) ?
  11877. node.parentElement :
  11878. node;
  11879. return this.textLineHeight ?
  11880. parseInt(this.textLineHeight.toString(), 10) :
  11881. this.renderer.fontMetrics(element || this.svgElement.element).h;
  11882. }
  11883. /**
  11884. * Transform a pseudo HTML AST node tree into an SVG structure. We do as
  11885. * much heavy lifting as we can here, before doing the final processing in
  11886. * the modifyDOM function. The original data is mutated.
  11887. *
  11888. * @private
  11889. *
  11890. * @param {ASTNode[]} nodes The AST nodes
  11891. *
  11892. */
  11893. modifyTree(nodes) {
  11894. const modifyChild = (node, i) => {
  11895. const { attributes = {}, children, style = {}, tagName } = node, styledMode = this.renderer.styledMode;
  11896. // Apply styling to text tags
  11897. if (tagName === 'b' || tagName === 'strong') {
  11898. if (styledMode) {
  11899. // eslint-disable-next-line dot-notation
  11900. attributes['class'] = 'highcharts-strong';
  11901. }
  11902. else {
  11903. style.fontWeight = 'bold';
  11904. }
  11905. }
  11906. else if (tagName === 'i' || tagName === 'em') {
  11907. if (styledMode) {
  11908. // eslint-disable-next-line dot-notation
  11909. attributes['class'] = 'highcharts-emphasized';
  11910. }
  11911. else {
  11912. style.fontStyle = 'italic';
  11913. }
  11914. }
  11915. // Modify styling
  11916. if (style && style.color) {
  11917. style.fill = style.color;
  11918. }
  11919. // Handle breaks
  11920. if (tagName === 'br') {
  11921. attributes['class'] = 'highcharts-br'; // eslint-disable-line dot-notation
  11922. node.textContent = '\u200B'; // zero-width space
  11923. // Trim whitespace off the beginning of new lines
  11924. const nextNode = nodes[i + 1];
  11925. if (nextNode && nextNode.textContent) {
  11926. nextNode.textContent =
  11927. nextNode.textContent.replace(/^ +/gm, '');
  11928. }
  11929. // If an anchor has direct text node children, the text is unable to
  11930. // wrap because there is no `getSubStringLength` function on the
  11931. // element. Therefore we need to wrap the child text node or nodes
  11932. // in a tspan. #16173.
  11933. }
  11934. else if (tagName === 'a' &&
  11935. children &&
  11936. children.some((child) => child.tagName === '#text')) {
  11937. node.children = [{ children, tagName: 'tspan' }];
  11938. }
  11939. if (tagName !== '#text' && tagName !== 'a') {
  11940. node.tagName = 'tspan';
  11941. }
  11942. extend(node, { attributes, style });
  11943. // Recurse
  11944. if (children) {
  11945. children
  11946. .filter((c) => c.tagName !== '#text')
  11947. .forEach(modifyChild);
  11948. }
  11949. };
  11950. nodes.forEach(modifyChild);
  11951. fireEvent(this.svgElement, 'afterModifyTree', { nodes });
  11952. }
  11953. /*
  11954. * Truncate the text node contents to a given length. Used when the css
  11955. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  11956. * character by character to the given length. If not, the text is
  11957. * word-wrapped line by line.
  11958. */
  11959. truncate(textNode, text, words, startAt, width, getString) {
  11960. const svgElement = this.svgElement;
  11961. const { renderer, rotation } = svgElement;
  11962. // Cache the lengths to avoid checking the same twice
  11963. const lengths = [];
  11964. // Word wrap cannot be truncated to shorter than one word, ellipsis
  11965. // text can be completely blank.
  11966. let minIndex = words ? 1 : 0;
  11967. let maxIndex = (text || words || '').length;
  11968. let currentIndex = maxIndex;
  11969. let str;
  11970. let actualWidth;
  11971. const getSubStringLength = function (charEnd, concatenatedEnd) {
  11972. // charEnd is used when finding the character-by-character
  11973. // break for ellipsis, concatenatedEnd is used for word-by-word
  11974. // break for word wrapping.
  11975. const end = concatenatedEnd || charEnd;
  11976. const parentNode = textNode.parentNode;
  11977. if (parentNode && typeof lengths[end] === 'undefined') {
  11978. // Modern browsers
  11979. if (parentNode.getSubStringLength) {
  11980. // Fails with DOM exception on unit-tests/legend/members
  11981. // of unknown reason. Desired width is 0, text content
  11982. // is "5" and end is 1.
  11983. try {
  11984. lengths[end] = startAt +
  11985. parentNode.getSubStringLength(0, words ? end + 1 : end);
  11986. }
  11987. catch (e) {
  11988. '';
  11989. }
  11990. }
  11991. }
  11992. return lengths[end];
  11993. };
  11994. svgElement.rotation = 0; // discard rotation when computing box
  11995. actualWidth = getSubStringLength(textNode.textContent.length);
  11996. if (startAt + actualWidth > width) {
  11997. // Do a binary search for the index where to truncate the text
  11998. while (minIndex <= maxIndex) {
  11999. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  12000. // When checking words for word-wrap, we need to build the
  12001. // string and measure the subStringLength at the concatenated
  12002. // word length.
  12003. if (words) {
  12004. str = getString(words, currentIndex);
  12005. }
  12006. actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
  12007. if (minIndex === maxIndex) {
  12008. // Complete
  12009. minIndex = maxIndex + 1;
  12010. }
  12011. else if (actualWidth > width) {
  12012. // Too large. Set max index to current.
  12013. maxIndex = currentIndex - 1;
  12014. }
  12015. else {
  12016. // Within width. Set min index to current.
  12017. minIndex = currentIndex;
  12018. }
  12019. }
  12020. // If max index was 0 it means the shortest possible text was also
  12021. // too large. For ellipsis that means only the ellipsis, while for
  12022. // word wrap it means the whole first word.
  12023. if (maxIndex === 0) {
  12024. // Remove ellipsis
  12025. textNode.textContent = '';
  12026. // If the new text length is one less than the original, we don't
  12027. // need the ellipsis
  12028. }
  12029. else if (!(text && maxIndex === text.length - 1)) {
  12030. textNode.textContent = str || getString(text || words, currentIndex);
  12031. }
  12032. }
  12033. // When doing line wrapping, prepare for the next line by removing the
  12034. // items from this line.
  12035. if (words) {
  12036. words.splice(0, currentIndex);
  12037. }
  12038. svgElement.actualWidth = actualWidth;
  12039. svgElement.rotation = rotation; // Apply rotation again.
  12040. }
  12041. /*
  12042. * Un-escape HTML entities based on the public `renderer.escapes` list
  12043. *
  12044. * @private
  12045. *
  12046. * @param {string} inputStr The string to unescape
  12047. * @param {Array<string>} [except] Exceptions
  12048. *
  12049. * @return {string} The processed string
  12050. */
  12051. unescapeEntities(inputStr, except) {
  12052. objectEach(this.renderer.escapes, function (value, key) {
  12053. if (!except || except.indexOf(value) === -1) {
  12054. inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
  12055. }
  12056. });
  12057. return inputStr;
  12058. }
  12059. }
  12060. return TextBuilder;
  12061. });
  12062. _registerModule(_modules, 'Core/Renderer/SVG/SVGRenderer.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGLabel.js'], _modules['Core/Renderer/SVG/Symbols.js'], _modules['Core/Renderer/SVG/TextBuilder.js'], _modules['Core/Utilities.js']], function (AST, Color, H, RendererRegistry, SVGElement, SVGLabel, Symbols, TextBuilder, U) {
  12063. /* *
  12064. *
  12065. * (c) 2010-2021 Torstein Honsi
  12066. *
  12067. * License: www.highcharts.com/license
  12068. *
  12069. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  12070. *
  12071. * */
  12072. const { charts, deg2rad, doc, isFirefox, isMS, isWebKit, noop, SVG_NS, symbolSizes, win } = H;
  12073. const { addEvent, attr, createElement, css, defined, destroyObjectProperties, extend, isArray, isNumber, isObject, isString, merge, pick, pInt, uniqueKey } = U;
  12074. /* *
  12075. *
  12076. * Variables
  12077. *
  12078. * */
  12079. let hasInternalReferenceBug;
  12080. /* *
  12081. *
  12082. * Class
  12083. *
  12084. * */
  12085. /* eslint-disable no-invalid-this, valid-jsdoc */
  12086. /**
  12087. * Allows direct access to the Highcharts rendering layer in order to draw
  12088. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  12089. * or independent from any chart. The SVGRenderer represents a wrapper object
  12090. * for SVG in modern browsers.
  12091. *
  12092. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  12093. * The renderer can also be used completely decoupled from a chart.
  12094. *
  12095. * @sample highcharts/members/renderer-on-chart
  12096. * Annotating a chart programmatically.
  12097. * @sample highcharts/members/renderer-basic
  12098. * Independent SVG drawing.
  12099. *
  12100. * @example
  12101. * // Use directly without a chart object.
  12102. * let renderer = new Highcharts.Renderer(parentNode, 600, 400);
  12103. *
  12104. * @class
  12105. * @name Highcharts.SVGRenderer
  12106. *
  12107. * @param {Highcharts.HTMLDOMElement} container
  12108. * Where to put the SVG in the web page.
  12109. *
  12110. * @param {number} width
  12111. * The width of the SVG.
  12112. *
  12113. * @param {number} height
  12114. * The height of the SVG.
  12115. *
  12116. * @param {Highcharts.CSSObject} [style]
  12117. * The box style, if not in styleMode
  12118. *
  12119. * @param {boolean} [forExport=false]
  12120. * Whether the rendered content is intended for export.
  12121. *
  12122. * @param {boolean} [allowHTML=true]
  12123. * Whether the renderer is allowed to include HTML text, which will be
  12124. * projected on top of the SVG.
  12125. *
  12126. * @param {boolean} [styledMode=false]
  12127. * Whether the renderer belongs to a chart that is in styled mode.
  12128. * If it does, it will avoid setting presentational attributes in
  12129. * some cases, but not when set explicitly through `.attr` and `.css`
  12130. * etc.
  12131. */
  12132. class SVGRenderer {
  12133. /* *
  12134. *
  12135. * Constructors
  12136. *
  12137. * */
  12138. constructor(container, width, height, style, forExport, allowHTML, styledMode) {
  12139. /* *
  12140. *
  12141. * Properties
  12142. *
  12143. * */
  12144. this.alignedObjects = void 0;
  12145. /**
  12146. * The root `svg` node of the renderer.
  12147. *
  12148. * @name Highcharts.SVGRenderer#box
  12149. * @type {Highcharts.SVGDOMElement}
  12150. */
  12151. this.box = void 0;
  12152. /**
  12153. * The wrapper for the root `svg` node of the renderer.
  12154. *
  12155. * @name Highcharts.SVGRenderer#boxWrapper
  12156. * @type {Highcharts.SVGElement}
  12157. */
  12158. this.boxWrapper = void 0;
  12159. this.cache = void 0;
  12160. this.cacheKeys = void 0;
  12161. this.chartIndex = void 0;
  12162. /**
  12163. * A pointer to the `defs` node of the root SVG.
  12164. *
  12165. * @name Highcharts.SVGRenderer#defs
  12166. * @type {Highcharts.SVGElement}
  12167. */
  12168. this.defs = void 0;
  12169. this.globalAnimation = void 0;
  12170. this.gradients = void 0;
  12171. this.height = void 0;
  12172. this.imgCount = void 0;
  12173. this.style = void 0;
  12174. /**
  12175. * Page url used for internal references.
  12176. *
  12177. * @private
  12178. * @name Highcharts.SVGRenderer#url
  12179. * @type {string}
  12180. */
  12181. this.url = void 0;
  12182. this.width = void 0;
  12183. this.init(container, width, height, style, forExport, allowHTML, styledMode);
  12184. }
  12185. /* *
  12186. *
  12187. * Functions
  12188. *
  12189. * */
  12190. /**
  12191. * Initialize the SVGRenderer. Overridable initializer function that takes
  12192. * the same parameters as the constructor.
  12193. *
  12194. * @function Highcharts.SVGRenderer#init
  12195. *
  12196. * @param {Highcharts.HTMLDOMElement} container
  12197. * Where to put the SVG in the web page.
  12198. *
  12199. * @param {number} width
  12200. * The width of the SVG.
  12201. *
  12202. * @param {number} height
  12203. * The height of the SVG.
  12204. *
  12205. * @param {Highcharts.CSSObject} [style]
  12206. * The box style, if not in styleMode
  12207. *
  12208. * @param {boolean} [forExport=false]
  12209. * Whether the rendered content is intended for export.
  12210. *
  12211. * @param {boolean} [allowHTML=true]
  12212. * Whether the renderer is allowed to include HTML text, which will be
  12213. * projected on top of the SVG.
  12214. *
  12215. * @param {boolean} [styledMode=false]
  12216. * Whether the renderer belongs to a chart that is in styled mode. If it
  12217. * does, it will avoid setting presentational attributes in some cases, but
  12218. * not when set explicitly through `.attr` and `.css` etc.
  12219. */
  12220. init(container, width, height, style, forExport, allowHTML, styledMode) {
  12221. const renderer = this, boxWrapper = renderer
  12222. .createElement('svg')
  12223. .attr({
  12224. version: '1.1',
  12225. 'class': 'highcharts-root'
  12226. }), element = boxWrapper.element;
  12227. if (!styledMode) {
  12228. boxWrapper.css(this.getStyle(style));
  12229. }
  12230. container.appendChild(element);
  12231. // Always use ltr on the container, otherwise text-anchor will be
  12232. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  12233. attr(container, 'dir', 'ltr');
  12234. // For browsers other than IE, add the namespace attribute (#1978)
  12235. if (container.innerHTML.indexOf('xmlns') === -1) {
  12236. attr(element, 'xmlns', this.SVG_NS);
  12237. }
  12238. this.box = element;
  12239. this.boxWrapper = boxWrapper;
  12240. renderer.alignedObjects = [];
  12241. this.url = this.getReferenceURL();
  12242. // Add description
  12243. const desc = this.createElement('desc').add();
  12244. desc.element.appendChild(doc.createTextNode('Created with Highcharts 11.1.0'));
  12245. renderer.defs = this.createElement('defs').add();
  12246. renderer.allowHTML = allowHTML;
  12247. renderer.forExport = forExport;
  12248. renderer.styledMode = styledMode;
  12249. renderer.gradients = {}; // Object where gradient SvgElements are stored
  12250. renderer.cache = {}; // Cache for numerical bounding boxes
  12251. renderer.cacheKeys = [];
  12252. renderer.imgCount = 0;
  12253. renderer.rootFontSize = boxWrapper.getStyle('font-size');
  12254. renderer.setSize(width, height, false);
  12255. // Issue 110 workaround:
  12256. // In Firefox, if a div is positioned by percentage, its pixel position
  12257. // may land between pixels. The container itself doesn't display this,
  12258. // but an SVG element inside this container will be drawn at subpixel
  12259. // precision. In order to draw sharp lines, this must be compensated
  12260. // for. This doesn't seem to work inside iframes though (like in
  12261. // jsFiddle).
  12262. let subPixelFix, rect;
  12263. if (isFirefox && container.getBoundingClientRect) {
  12264. subPixelFix = function () {
  12265. css(container, { left: 0, top: 0 });
  12266. rect = container.getBoundingClientRect();
  12267. css(container, {
  12268. left: (Math.ceil(rect.left) - rect.left) + 'px',
  12269. top: (Math.ceil(rect.top) - rect.top) + 'px'
  12270. });
  12271. };
  12272. // run the fix now
  12273. subPixelFix();
  12274. // run it on resize
  12275. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  12276. }
  12277. }
  12278. /**
  12279. * General method for adding a definition to the SVG `defs` tag. Can be used
  12280. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  12281. * general definitions to the SVG's defs tag. Definitions can be referenced
  12282. * from the CSS by its `id`. Read more in
  12283. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  12284. * Styled mode only.
  12285. *
  12286. * @function Highcharts.SVGRenderer#definition
  12287. *
  12288. * @param {Highcharts.ASTNode} def
  12289. * A serialized form of an SVG definition, including children.
  12290. *
  12291. * @return {Highcharts.SVGElement}
  12292. * The inserted node.
  12293. */
  12294. definition(def) {
  12295. const ast = new AST([def]);
  12296. return ast.addToDOM(this.defs.element);
  12297. }
  12298. /**
  12299. * Get the prefix needed for internal URL references to work in certain
  12300. * cases. Some older browser versions had a bug where internal url
  12301. * references in SVG attributes, on the form `url(#some-id)`, would fail if
  12302. * a base tag was present in the page. There were also issues with
  12303. * `history.pushState` related to this prefix.
  12304. *
  12305. * Related issues: #24, #672, #1070, #5244.
  12306. *
  12307. * The affected browsers are:
  12308. * - Chrome <= 53 (May 2018)
  12309. * - Firefox <= 51 (January 2017)
  12310. * - Safari/Mac <= 12.1 (2018 or 2019)
  12311. * - Safari/iOS <= 13
  12312. *
  12313. * @todo Remove this hack when time has passed. All the affected browsers
  12314. * are evergreens, so it is increasingly unlikely that users are affected by
  12315. * the bug.
  12316. *
  12317. * @return {string}
  12318. * The prefix to use. An empty string for modern browsers.
  12319. */
  12320. getReferenceURL() {
  12321. if ((isFirefox || isWebKit) &&
  12322. doc.getElementsByTagName('base').length) {
  12323. // Detect if a clip path is taking effect by performing a hit test
  12324. // outside the clipped area. If the hit element is the rectangle
  12325. // that was supposed to be clipped, the bug is present. This only
  12326. // has to be performed once per page load, so we store the result
  12327. // locally in the module.
  12328. if (!defined(hasInternalReferenceBug)) {
  12329. const id = uniqueKey();
  12330. const ast = new AST([{
  12331. tagName: 'svg',
  12332. attributes: {
  12333. width: 8,
  12334. height: 8
  12335. },
  12336. children: [{
  12337. tagName: 'defs',
  12338. children: [{
  12339. tagName: 'clipPath',
  12340. attributes: {
  12341. id
  12342. },
  12343. children: [{
  12344. tagName: 'rect',
  12345. attributes: {
  12346. width: 4,
  12347. height: 4
  12348. }
  12349. }]
  12350. }]
  12351. }, {
  12352. tagName: 'rect',
  12353. attributes: {
  12354. id: 'hitme',
  12355. width: 8,
  12356. height: 8,
  12357. 'clip-path': `url(#${id})`,
  12358. fill: 'rgba(0,0,0,0.001)'
  12359. }
  12360. }]
  12361. }]);
  12362. const svg = ast.addToDOM(doc.body);
  12363. css(svg, {
  12364. position: 'fixed',
  12365. top: 0,
  12366. left: 0,
  12367. zIndex: 9e5
  12368. });
  12369. const hitElement = doc.elementFromPoint(6, 6);
  12370. hasInternalReferenceBug = (hitElement && hitElement.id) === 'hitme';
  12371. doc.body.removeChild(svg);
  12372. }
  12373. if (hasInternalReferenceBug) {
  12374. return win.location.href
  12375. .split('#')[0] // remove the hash
  12376. .replace(/<[^>]*>/g, '') // wing cut HTML
  12377. // escape parantheses and quotes
  12378. .replace(/([\('\)])/g, '\\$1')
  12379. // replace spaces (needed for Safari only)
  12380. .replace(/ /g, '%20');
  12381. }
  12382. }
  12383. return '';
  12384. }
  12385. /**
  12386. * Get the global style setting for the renderer.
  12387. *
  12388. * @private
  12389. * @function Highcharts.SVGRenderer#getStyle
  12390. *
  12391. * @param {Highcharts.CSSObject} style
  12392. * Style settings.
  12393. *
  12394. * @return {Highcharts.CSSObject}
  12395. * The style settings mixed with defaults.
  12396. */
  12397. getStyle(style) {
  12398. this.style = extend({
  12399. fontFamily: 'Helvetica, Arial, sans-serif',
  12400. fontSize: '1rem'
  12401. }, style);
  12402. return this.style;
  12403. }
  12404. /**
  12405. * Apply the global style on the renderer, mixed with the default styles.
  12406. *
  12407. * @function Highcharts.SVGRenderer#setStyle
  12408. *
  12409. * @param {Highcharts.CSSObject} style
  12410. * CSS to apply.
  12411. */
  12412. setStyle(style) {
  12413. this.boxWrapper.css(this.getStyle(style));
  12414. }
  12415. /**
  12416. * Detect whether the renderer is hidden. This happens when one of the
  12417. * parent elements has `display: none`. Used internally to detect when we
  12418. * needto render preliminarily in another div to get the text bounding boxes
  12419. * right.
  12420. *
  12421. * @function Highcharts.SVGRenderer#isHidden
  12422. *
  12423. * @return {boolean}
  12424. * True if it is hidden.
  12425. */
  12426. isHidden() {
  12427. return !this.boxWrapper.getBBox().width;
  12428. }
  12429. /**
  12430. * Destroys the renderer and its allocated members.
  12431. *
  12432. * @function Highcharts.SVGRenderer#destroy
  12433. *
  12434. * @return {null}
  12435. * Pass through value.
  12436. */
  12437. destroy() {
  12438. const renderer = this, rendererDefs = renderer.defs;
  12439. renderer.box = null;
  12440. renderer.boxWrapper = renderer.boxWrapper.destroy();
  12441. // Call destroy on all gradient elements
  12442. destroyObjectProperties(renderer.gradients || {});
  12443. renderer.gradients = null;
  12444. renderer.defs = rendererDefs.destroy();
  12445. // Remove sub pixel fix handler (#982)
  12446. if (renderer.unSubPixelFix) {
  12447. renderer.unSubPixelFix();
  12448. }
  12449. renderer.alignedObjects = null;
  12450. return null;
  12451. }
  12452. /**
  12453. * Create a wrapper for an SVG element. Serves as a factory for
  12454. * {@link SVGElement}, but this function is itself mostly called from
  12455. * primitive factories like {@link SVGRenderer#path}, {@link
  12456. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  12457. *
  12458. * @function Highcharts.SVGRenderer#createElement
  12459. *
  12460. * @param {string} nodeName
  12461. * The node name, for example `rect`, `g` etc.
  12462. *
  12463. * @return {Highcharts.SVGElement}
  12464. * The generated SVGElement.
  12465. */
  12466. createElement(nodeName) {
  12467. const wrapper = new this.Element();
  12468. wrapper.init(this, nodeName);
  12469. return wrapper;
  12470. }
  12471. /**
  12472. * Get converted radial gradient attributes according to the radial
  12473. * reference. Used internally from the {@link SVGElement#colorGradient}
  12474. * function.
  12475. *
  12476. * @private
  12477. * @function Highcharts.SVGRenderer#getRadialAttr
  12478. */
  12479. getRadialAttr(radialReference, gradAttr) {
  12480. return {
  12481. cx: (radialReference[0] - radialReference[2] / 2) +
  12482. (gradAttr.cx || 0) * radialReference[2],
  12483. cy: (radialReference[1] - radialReference[2] / 2) +
  12484. (gradAttr.cy || 0) * radialReference[2],
  12485. r: (gradAttr.r || 0) * radialReference[2]
  12486. };
  12487. }
  12488. /**
  12489. * Create a drop shadow definition and return its id
  12490. *
  12491. * @private
  12492. * @function Highcharts.SVGRenderer#shadowDefinition
  12493. *
  12494. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
  12495. * shadow options. If `true`, the default options are applied
  12496. */
  12497. shadowDefinition(shadowOptions) {
  12498. const id = [
  12499. `highcharts-drop-shadow-${this.chartIndex}`,
  12500. ...Object.keys(shadowOptions)
  12501. .map((key) => shadowOptions[key])
  12502. ].join('-').replace(/[^a-z0-9\-]/g, ''), options = merge({
  12503. color: '#000000',
  12504. offsetX: 1,
  12505. offsetY: 1,
  12506. opacity: 0.15,
  12507. width: 5
  12508. }, shadowOptions);
  12509. if (!this.defs.element.querySelector(`#${id}`)) {
  12510. this.definition({
  12511. tagName: 'filter',
  12512. attributes: {
  12513. id
  12514. },
  12515. children: [{
  12516. tagName: 'feDropShadow',
  12517. attributes: {
  12518. dx: options.offsetX,
  12519. dy: options.offsetY,
  12520. 'flood-color': options.color,
  12521. // Tuned and modified to keep a preserve compatibility
  12522. // with the old settings
  12523. 'flood-opacity': Math.min(options.opacity * 5, 1),
  12524. stdDeviation: options.width / 2
  12525. }
  12526. }]
  12527. });
  12528. }
  12529. return id;
  12530. }
  12531. /**
  12532. * Parse a simple HTML string into SVG tspans. Called internally when text
  12533. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  12534. * text features like `width`, `text-overflow`, `white-space`, and also
  12535. * attributes like `href` and `style`.
  12536. *
  12537. * @private
  12538. * @function Highcharts.SVGRenderer#buildText
  12539. *
  12540. * @param {Highcharts.SVGElement} wrapper
  12541. * The parent SVGElement.
  12542. */
  12543. buildText(wrapper) {
  12544. new TextBuilder(wrapper).buildSVG();
  12545. }
  12546. /**
  12547. * Returns white for dark colors and black for bright colors, based on W3C's
  12548. * definition of [Relative luminance](
  12549. * https://www.w3.org/WAI/GL/wiki/Relative_luminance).
  12550. *
  12551. * @function Highcharts.SVGRenderer#getContrast
  12552. *
  12553. * @param {Highcharts.ColorString} color
  12554. * The color to get the contrast for.
  12555. *
  12556. * @return {Highcharts.ColorString}
  12557. * The contrast color, either `#000000` or `#FFFFFF`.
  12558. */
  12559. getContrast(color) {
  12560. // #6216, #17273
  12561. const rgba = Color.parse(color).rgba
  12562. .map((b8) => {
  12563. const c = b8 / 255;
  12564. return c <= 0.03928 ?
  12565. c / 12.92 :
  12566. Math.pow((c + 0.055) / 1.055, 2.4);
  12567. });
  12568. // Relative luminance
  12569. const l = 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2];
  12570. // Use white or black based on which provides more contrast
  12571. return 1.05 / (l + 0.05) > (l + 0.05) / 0.05 ? '#FFFFFF' : '#000000';
  12572. }
  12573. /**
  12574. * Create a button with preset states.
  12575. *
  12576. * @function Highcharts.SVGRenderer#button
  12577. *
  12578. * @param {string} text
  12579. * The text or HTML to draw.
  12580. *
  12581. * @param {number} x
  12582. * The x position of the button's left side.
  12583. *
  12584. * @param {number} y
  12585. * The y position of the button's top side.
  12586. *
  12587. * @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
  12588. * The function to execute on button click or touch.
  12589. *
  12590. * @param {Highcharts.SVGAttributes} [theme]
  12591. * SVG attributes for the normal state.
  12592. *
  12593. * @param {Highcharts.SVGAttributes} [hoverState]
  12594. * SVG attributes for the hover state.
  12595. *
  12596. * @param {Highcharts.SVGAttributes} [selectState]
  12597. * SVG attributes for the pressed state.
  12598. *
  12599. * @param {Highcharts.SVGAttributes} [disabledState]
  12600. * SVG attributes for the disabled state.
  12601. *
  12602. * @param {Highcharts.SymbolKeyValue} [shape=rect]
  12603. * The shape type.
  12604. *
  12605. * @param {boolean} [useHTML=false]
  12606. * Whether to use HTML to render the label.
  12607. *
  12608. * @return {Highcharts.SVGElement}
  12609. * The button element.
  12610. */
  12611. button(text, x, y, callback, theme = {}, hoverState, selectState, disabledState, shape, useHTML) {
  12612. const label = this.label(text, x, y, shape, void 0, void 0, useHTML, void 0, 'button'), styledMode = this.styledMode, states = theme.states || {};
  12613. let curState = 0;
  12614. theme = merge(theme);
  12615. delete theme.states;
  12616. const normalStyle = merge({
  12617. color: "#333333" /* Palette.neutralColor80 */,
  12618. cursor: 'pointer',
  12619. fontSize: '0.8em',
  12620. fontWeight: 'normal'
  12621. }, theme.style);
  12622. delete theme.style;
  12623. // Remove stylable attributes. Pass in the ButtonThemeObject and get the
  12624. // SVGAttributes subset back.
  12625. let normalState = AST.filterUserAttributes(theme);
  12626. // Default, non-stylable attributes
  12627. label.attr(merge({ padding: 8, r: 2 }, normalState));
  12628. // Presentational. The string type is a mistake, it is just for
  12629. // compliance with SVGAttribute and is not used in button theme.
  12630. let hoverStyle, selectStyle, disabledStyle;
  12631. if (!styledMode) {
  12632. // Normal state - prepare the attributes
  12633. normalState = merge({
  12634. fill: "#f7f7f7" /* Palette.neutralColor3 */,
  12635. stroke: "#cccccc" /* Palette.neutralColor20 */,
  12636. 'stroke-width': 1
  12637. }, normalState);
  12638. // Hover state
  12639. hoverState = merge(normalState, {
  12640. fill: "#e6e6e6" /* Palette.neutralColor10 */
  12641. }, AST.filterUserAttributes(hoverState || states.hover || {}));
  12642. hoverStyle = hoverState.style;
  12643. delete hoverState.style;
  12644. // Pressed state
  12645. selectState = merge(normalState, {
  12646. fill: "#e6e9ff" /* Palette.highlightColor10 */,
  12647. style: {
  12648. color: "#000000" /* Palette.neutralColor100 */,
  12649. fontWeight: 'bold'
  12650. }
  12651. }, AST.filterUserAttributes(selectState || states.select || {}));
  12652. selectStyle = selectState.style;
  12653. delete selectState.style;
  12654. // Disabled state
  12655. disabledState = merge(normalState, {
  12656. style: {
  12657. color: "#cccccc" /* Palette.neutralColor20 */
  12658. }
  12659. }, AST.filterUserAttributes(disabledState || states.disabled || {}));
  12660. disabledStyle = disabledState.style;
  12661. delete disabledState.style;
  12662. }
  12663. // Add the events. IE9 and IE10 need mouseover and mouseout to function
  12664. // (#667).
  12665. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  12666. if (curState !== 3) {
  12667. label.setState(1);
  12668. }
  12669. });
  12670. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  12671. if (curState !== 3) {
  12672. label.setState(curState);
  12673. }
  12674. });
  12675. label.setState = function (state) {
  12676. // Hover state is temporary, don't record it
  12677. if (state !== 1) {
  12678. label.state = curState = state;
  12679. }
  12680. // Update visuals
  12681. label
  12682. .removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
  12683. .addClass('highcharts-button-' +
  12684. ['normal', 'hover', 'pressed', 'disabled'][state || 0]);
  12685. if (!styledMode) {
  12686. label
  12687. .attr([
  12688. normalState,
  12689. hoverState,
  12690. selectState,
  12691. disabledState
  12692. ][state || 0]);
  12693. const css = [
  12694. normalStyle,
  12695. hoverStyle,
  12696. selectStyle,
  12697. disabledStyle
  12698. ][state || 0];
  12699. if (isObject(css)) {
  12700. label.css(css);
  12701. }
  12702. }
  12703. };
  12704. // Presentational attributes
  12705. if (!styledMode) {
  12706. label
  12707. .attr(normalState)
  12708. .css(extend({ cursor: 'default' }, normalStyle));
  12709. // HTML labels don't need to handle pointer events because click and
  12710. // mouseenter/mouseleave is bound to the underlying <g> element.
  12711. // Should this be reconsidered, we need more complex logic to share
  12712. // events between the <g> and its <div> counterpart, and avoid
  12713. // triggering mouseenter/mouseleave when hovering from one to the
  12714. // other (#17440).
  12715. if (useHTML) {
  12716. label.text.css({ pointerEvents: 'none' });
  12717. }
  12718. }
  12719. return label
  12720. .on('touchstart', (e) => e.stopPropagation())
  12721. .on('click', function (e) {
  12722. if (curState !== 3) {
  12723. callback.call(label, e);
  12724. }
  12725. });
  12726. }
  12727. /**
  12728. * Make a straight line crisper by not spilling out to neighbour pixels.
  12729. *
  12730. * @function Highcharts.SVGRenderer#crispLine
  12731. *
  12732. * @param {Highcharts.SVGPathArray} points
  12733. * The original points on the format `[['M', 0, 0], ['L', 100, 0]]`.
  12734. *
  12735. * @param {number} width
  12736. * The width of the line.
  12737. *
  12738. * @param {string} [roundingFunction=round]
  12739. * The rounding function name on the `Math` object, can be one of
  12740. * `round`, `floor` or `ceil`.
  12741. *
  12742. * @return {Highcharts.SVGPathArray}
  12743. * The original points array, but modified to render crisply.
  12744. */
  12745. crispLine(points, width, roundingFunction = 'round') {
  12746. const start = points[0];
  12747. const end = points[1];
  12748. // Normalize to a crisp line
  12749. if (defined(start[1]) && start[1] === end[1]) {
  12750. // Substract due to #1129. Now bottom and left axis gridlines behave
  12751. // the same.
  12752. start[1] = end[1] =
  12753. Math[roundingFunction](start[1]) - (width % 2 / 2);
  12754. }
  12755. if (defined(start[2]) && start[2] === end[2]) {
  12756. start[2] = end[2] =
  12757. Math[roundingFunction](start[2]) + (width % 2 / 2);
  12758. }
  12759. return points;
  12760. }
  12761. /**
  12762. * Draw a path, wraps the SVG `path` element.
  12763. *
  12764. * @sample highcharts/members/renderer-path-on-chart/
  12765. * Draw a path in a chart
  12766. * @sample highcharts/members/renderer-path/
  12767. * Draw a path independent from a chart
  12768. *
  12769. * @example
  12770. * let path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  12771. * .attr({ stroke: '#ff00ff' })
  12772. * .add();
  12773. *
  12774. * @function Highcharts.SVGRenderer#path
  12775. *
  12776. * @param {Highcharts.SVGPathArray} [path]
  12777. * An SVG path definition in array form.
  12778. *
  12779. * @return {Highcharts.SVGElement}
  12780. * The generated wrapper element.
  12781. *
  12782. */ /**
  12783. * Draw a path, wraps the SVG `path` element.
  12784. *
  12785. * @function Highcharts.SVGRenderer#path
  12786. *
  12787. * @param {Highcharts.SVGAttributes} [attribs]
  12788. * The initial attributes.
  12789. *
  12790. * @return {Highcharts.SVGElement}
  12791. * The generated wrapper element.
  12792. */
  12793. path(path) {
  12794. const attribs = (this.styledMode ? {} : {
  12795. fill: 'none'
  12796. });
  12797. if (isArray(path)) {
  12798. attribs.d = path;
  12799. }
  12800. else if (isObject(path)) { // attributes
  12801. extend(attribs, path);
  12802. }
  12803. return this.createElement('path').attr(attribs);
  12804. }
  12805. /**
  12806. * Draw a circle, wraps the SVG `circle` element.
  12807. *
  12808. * @sample highcharts/members/renderer-circle/
  12809. * Drawing a circle
  12810. *
  12811. * @function Highcharts.SVGRenderer#circle
  12812. *
  12813. * @param {number} [x]
  12814. * The center x position.
  12815. *
  12816. * @param {number} [y]
  12817. * The center y position.
  12818. *
  12819. * @param {number} [r]
  12820. * The radius.
  12821. *
  12822. * @return {Highcharts.SVGElement}
  12823. * The generated wrapper element.
  12824. */ /**
  12825. * Draw a circle, wraps the SVG `circle` element.
  12826. *
  12827. * @function Highcharts.SVGRenderer#circle
  12828. *
  12829. * @param {Highcharts.SVGAttributes} [attribs]
  12830. * The initial attributes.
  12831. *
  12832. * @return {Highcharts.SVGElement}
  12833. * The generated wrapper element.
  12834. */
  12835. circle(x, y, r) {
  12836. const attribs = (isObject(x) ?
  12837. x :
  12838. typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
  12839. // Setting x or y translates to cx and cy
  12840. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  12841. element.setAttribute('c' + key, value);
  12842. };
  12843. return wrapper.attr(attribs);
  12844. }
  12845. /**
  12846. * Draw and return an arc.
  12847. *
  12848. * @sample highcharts/members/renderer-arc/
  12849. * Drawing an arc
  12850. *
  12851. * @function Highcharts.SVGRenderer#arc
  12852. *
  12853. * @param {number} [x=0]
  12854. * Center X position.
  12855. *
  12856. * @param {number} [y=0]
  12857. * Center Y position.
  12858. *
  12859. * @param {number} [r=0]
  12860. * The outer radius' of the arc.
  12861. *
  12862. * @param {number} [innerR=0]
  12863. * Inner radius like used in donut charts.
  12864. *
  12865. * @param {number} [start=0]
  12866. * The starting angle of the arc in radians, where 0 is to the right and
  12867. * `-Math.PI/2` is up.
  12868. *
  12869. * @param {number} [end=0]
  12870. * The ending angle of the arc in radians, where 0 is to the right and
  12871. * `-Math.PI/2` is up.
  12872. *
  12873. * @return {Highcharts.SVGElement}
  12874. * The generated wrapper element.
  12875. */ /**
  12876. * Draw and return an arc. Overloaded function that takes arguments object.
  12877. *
  12878. * @function Highcharts.SVGRenderer#arc
  12879. *
  12880. * @param {Highcharts.SVGAttributes} attribs
  12881. * Initial SVG attributes.
  12882. *
  12883. * @return {Highcharts.SVGElement}
  12884. * The generated wrapper element.
  12885. */
  12886. arc(x, y, r, innerR, start, end) {
  12887. let options;
  12888. if (isObject(x)) {
  12889. options = x;
  12890. y = options.y;
  12891. r = options.r;
  12892. innerR = options.innerR;
  12893. start = options.start;
  12894. end = options.end;
  12895. x = options.x;
  12896. }
  12897. else {
  12898. options = { innerR, start, end };
  12899. }
  12900. // Arcs are defined as symbols for the ability to set
  12901. // attributes in attr and animate
  12902. const arc = this.symbol('arc', x, y, r, r, options);
  12903. arc.r = r; // #959
  12904. return arc;
  12905. }
  12906. /**
  12907. * Draw and return a rectangle.
  12908. *
  12909. * @function Highcharts.SVGRenderer#rect
  12910. *
  12911. * @param {number} [x]
  12912. * Left position.
  12913. *
  12914. * @param {number} [y]
  12915. * Top position.
  12916. *
  12917. * @param {number} [width]
  12918. * Width of the rectangle.
  12919. *
  12920. * @param {number} [height]
  12921. * Height of the rectangle.
  12922. *
  12923. * @param {number} [r]
  12924. * Border corner radius.
  12925. *
  12926. * @param {number} [strokeWidth]
  12927. * A stroke width can be supplied to allow crisp drawing.
  12928. *
  12929. * @return {Highcharts.SVGElement}
  12930. * The generated wrapper element.
  12931. */ /**
  12932. * Draw and return a rectangle.
  12933. *
  12934. * @sample highcharts/members/renderer-rect-on-chart/
  12935. * Draw a rectangle in a chart
  12936. * @sample highcharts/members/renderer-rect/
  12937. * Draw a rectangle independent from a chart
  12938. *
  12939. * @function Highcharts.SVGRenderer#rect
  12940. *
  12941. * @param {Highcharts.SVGAttributes} [attributes]
  12942. * General SVG attributes for the rectangle.
  12943. *
  12944. * @return {Highcharts.SVGElement}
  12945. * The generated wrapper element.
  12946. */
  12947. rect(x, y, width, height, r, strokeWidth) {
  12948. const attribs = (isObject(x) ?
  12949. x :
  12950. typeof x === 'undefined' ?
  12951. {} :
  12952. {
  12953. x,
  12954. y,
  12955. r,
  12956. width: Math.max(width || 0, 0),
  12957. height: Math.max(height || 0, 0)
  12958. }), wrapper = this.createElement('rect');
  12959. if (!this.styledMode) {
  12960. if (typeof strokeWidth !== 'undefined') {
  12961. attribs['stroke-width'] = strokeWidth;
  12962. extend(attribs, wrapper.crisp(attribs));
  12963. }
  12964. attribs.fill = 'none';
  12965. }
  12966. wrapper.rSetter = function (value, _key, element) {
  12967. wrapper.r = value;
  12968. attr(element, {
  12969. rx: value,
  12970. ry: value
  12971. });
  12972. };
  12973. wrapper.rGetter = function () {
  12974. return wrapper.r || 0;
  12975. };
  12976. return wrapper.attr(attribs);
  12977. }
  12978. /**
  12979. * Draw and return a rectangle with advanced corner rounding options.
  12980. *
  12981. * @function Highcharts.SVGRenderer#roundedRect
  12982. *
  12983. * @param {Highcharts.SVGAttributes} attribs
  12984. * Attributes
  12985. * @return {Highcharts.SVGElement}
  12986. * The generated wrapper element.
  12987. */
  12988. roundedRect(attribs) {
  12989. return this.symbol('roundedRect').attr(attribs);
  12990. }
  12991. /**
  12992. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  12993. * elements.
  12994. *
  12995. * @sample highcharts/members/renderer-g/
  12996. * Show and hide grouped objects
  12997. *
  12998. * @function Highcharts.SVGRenderer#setSize
  12999. *
  13000. * @param {number} width
  13001. * The new pixel width.
  13002. *
  13003. * @param {number} height
  13004. * The new pixel height.
  13005. *
  13006. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animate=true]
  13007. * Whether and how to animate.
  13008. */
  13009. setSize(width, height, animate) {
  13010. const renderer = this;
  13011. renderer.width = width;
  13012. renderer.height = height;
  13013. renderer.boxWrapper.animate({
  13014. width: width,
  13015. height: height
  13016. }, {
  13017. step: function () {
  13018. this.attr({
  13019. viewBox: '0 0 ' + this.attr('width') + ' ' +
  13020. this.attr('height')
  13021. });
  13022. },
  13023. duration: pick(animate, true) ? void 0 : 0
  13024. });
  13025. renderer.alignElements();
  13026. }
  13027. /**
  13028. * Create and return an svg group element. Child
  13029. * {@link Highcharts.SVGElement} objects are added to the group by using the
  13030. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  13031. *
  13032. * @function Highcharts.SVGRenderer#g
  13033. *
  13034. * @param {string} [name]
  13035. * The group will be given a class name of `highcharts-{name}`. This
  13036. * can be used for styling and scripting.
  13037. *
  13038. * @return {Highcharts.SVGElement}
  13039. * The generated wrapper element.
  13040. */
  13041. g(name) {
  13042. const elem = this.createElement('g');
  13043. return name ?
  13044. elem.attr({ 'class': 'highcharts-' + name }) :
  13045. elem;
  13046. }
  13047. /**
  13048. * Display an image.
  13049. *
  13050. * @sample highcharts/members/renderer-image-on-chart/
  13051. * Add an image in a chart
  13052. * @sample highcharts/members/renderer-image/
  13053. * Add an image independent of a chart
  13054. *
  13055. * @function Highcharts.SVGRenderer#image
  13056. *
  13057. * @param {string} href
  13058. * The image source.
  13059. *
  13060. * @param {number} [x]
  13061. * The X position.
  13062. *
  13063. * @param {number} [y]
  13064. * The Y position.
  13065. *
  13066. * @param {number} [width]
  13067. * The image width. If omitted, it defaults to the image file width.
  13068. *
  13069. * @param {number} [height]
  13070. * The image height. If omitted it defaults to the image file
  13071. * height.
  13072. *
  13073. * @param {Function} [onload]
  13074. * Event handler for image load.
  13075. *
  13076. * @return {Highcharts.SVGElement}
  13077. * The generated wrapper element.
  13078. */
  13079. image(href, x, y, width, height, onload) {
  13080. const attribs = { preserveAspectRatio: 'none' };
  13081. // Optional properties (#11756)
  13082. if (isNumber(x)) {
  13083. attribs.x = x;
  13084. }
  13085. if (isNumber(y)) {
  13086. attribs.y = y;
  13087. }
  13088. if (isNumber(width)) {
  13089. attribs.width = width;
  13090. }
  13091. if (isNumber(height)) {
  13092. attribs.height = height;
  13093. }
  13094. const elemWrapper = this.createElement('image').attr(attribs), onDummyLoad = function (e) {
  13095. elemWrapper.attr({ href });
  13096. onload.call(elemWrapper, e);
  13097. };
  13098. // Add load event if supplied
  13099. if (onload) {
  13100. // We have to use a dummy HTML image since IE support for SVG image
  13101. // load events is very buggy. First set a transparent src, wait for
  13102. // dummy to load, and then add the real src to the SVG image.
  13103. elemWrapper.attr({
  13104. /* eslint-disable-next-line max-len */
  13105. href: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
  13106. });
  13107. const dummy = new win.Image();
  13108. addEvent(dummy, 'load', onDummyLoad);
  13109. dummy.src = href;
  13110. if (dummy.complete) {
  13111. onDummyLoad({});
  13112. }
  13113. }
  13114. else {
  13115. elemWrapper.attr({ href });
  13116. }
  13117. return elemWrapper;
  13118. }
  13119. /**
  13120. * Draw a symbol out of pre-defined shape paths from
  13121. * {@link SVGRenderer#symbols}.
  13122. * It is used in Highcharts for point makers, which cake a `symbol` option,
  13123. * and label and button backgrounds like in the tooltip and stock flags.
  13124. *
  13125. * @function Highcharts.SVGRenderer#symbol
  13126. *
  13127. * @param {string} symbol
  13128. * The symbol name.
  13129. *
  13130. * @param {number} [x]
  13131. * The X coordinate for the top left position.
  13132. *
  13133. * @param {number} [y]
  13134. * The Y coordinate for the top left position.
  13135. *
  13136. * @param {number} [width]
  13137. * The pixel width.
  13138. *
  13139. * @param {number} [height]
  13140. * The pixel height.
  13141. *
  13142. * @param {Highcharts.SymbolOptionsObject} [options]
  13143. * Additional options, depending on the actual symbol drawn.
  13144. *
  13145. * @return {Highcharts.SVGElement}
  13146. * SVG symbol.
  13147. */
  13148. symbol(symbol, x, y, width, height, options) {
  13149. const ren = this, imageRegex = /^url\((.*?)\)$/, isImage = imageRegex.test(symbol), sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
  13150. // get the symbol definition function
  13151. symbolFn = (sym && this.symbols[sym]);
  13152. let obj, path, imageSrc, centerImage;
  13153. if (symbolFn) {
  13154. // Check if there's a path defined for this symbol
  13155. if (typeof x === 'number') {
  13156. path = symbolFn.call(this.symbols, Math.round(x || 0), Math.round(y || 0), width || 0, height || 0, options);
  13157. }
  13158. obj = this.path(path);
  13159. if (!ren.styledMode) {
  13160. obj.attr('fill', 'none');
  13161. }
  13162. // expando properties for use in animate and attr
  13163. extend(obj, {
  13164. symbolName: (sym || void 0),
  13165. x: x,
  13166. y: y,
  13167. width: width,
  13168. height: height
  13169. });
  13170. if (options) {
  13171. extend(obj, options);
  13172. }
  13173. // Image symbols
  13174. }
  13175. else if (isImage) {
  13176. imageSrc = symbol.match(imageRegex)[1];
  13177. // Create the image synchronously, add attribs async
  13178. const img = obj = this.image(imageSrc);
  13179. // The image width is not always the same as the symbol width. The
  13180. // image may be centered within the symbol, as is the case when
  13181. // image shapes are used as label backgrounds, for example in flags.
  13182. img.imgwidth = pick(options && options.width, symbolSizes[imageSrc] && symbolSizes[imageSrc].width);
  13183. img.imgheight = pick(options && options.height, symbolSizes[imageSrc] && symbolSizes[imageSrc].height);
  13184. /**
  13185. * Set the size and position
  13186. */
  13187. centerImage = (obj) => obj.attr({
  13188. width: obj.width,
  13189. height: obj.height
  13190. });
  13191. /**
  13192. * Width and height setters that take both the image's physical size
  13193. * and the label size into consideration, and translates the image
  13194. * to center within the label.
  13195. */
  13196. ['width', 'height'].forEach(function (key) {
  13197. img[key + 'Setter'] = function (value, key) {
  13198. this[key] = value;
  13199. const { alignByTranslate, element, width, height, imgwidth, imgheight } = this;
  13200. let imgSize = this['img' + key];
  13201. if (defined(imgSize)) {
  13202. let scale = 1;
  13203. // Scale and center the image within its container.
  13204. // The name `backgroundSize` is taken from the CSS spec,
  13205. // but the value `within` is made up. Other possible
  13206. // values in the spec, `cover` and `contain`, can be
  13207. // implemented if needed.
  13208. if (options &&
  13209. options.backgroundSize === 'within' &&
  13210. width &&
  13211. height) {
  13212. scale = Math.min(width / imgwidth, height / imgheight);
  13213. imgSize = Math.round(imgSize * scale);
  13214. // Update both width and height to keep the ratio
  13215. // correct (#17315)
  13216. attr(element, {
  13217. width: Math.round(imgwidth * scale),
  13218. height: Math.round(imgheight * scale)
  13219. });
  13220. }
  13221. else if (element) {
  13222. element.setAttribute(key, imgSize);
  13223. }
  13224. if (!alignByTranslate) {
  13225. this.translate(((width || 0) - (imgwidth * scale)) / 2, ((height || 0) - (imgheight * scale)) / 2);
  13226. }
  13227. }
  13228. };
  13229. });
  13230. if (defined(x)) {
  13231. img.attr({
  13232. x: x,
  13233. y: y
  13234. });
  13235. }
  13236. img.isImg = true;
  13237. if (defined(img.imgwidth) && defined(img.imgheight)) {
  13238. centerImage(img);
  13239. }
  13240. else {
  13241. // Initialize image to be 0 size so export will still function
  13242. // if there's no cached sizes.
  13243. img.attr({ width: 0, height: 0 });
  13244. // Create a dummy JavaScript image to get the width and height.
  13245. createElement('img', {
  13246. onload: function () {
  13247. const chart = charts[ren.chartIndex];
  13248. // Special case for SVGs on IE11, the width is not
  13249. // accessible until the image is part of the DOM
  13250. // (#2854).
  13251. if (this.width === 0) {
  13252. css(this, {
  13253. position: 'absolute',
  13254. top: '-999em'
  13255. });
  13256. doc.body.appendChild(this);
  13257. }
  13258. // Center the image
  13259. symbolSizes[imageSrc] = {
  13260. width: this.width,
  13261. height: this.height
  13262. };
  13263. img.imgwidth = this.width;
  13264. img.imgheight = this.height;
  13265. if (img.element) {
  13266. centerImage(img);
  13267. }
  13268. // Clean up after #2854 workaround.
  13269. if (this.parentNode) {
  13270. this.parentNode.removeChild(this);
  13271. }
  13272. // Fire the load event when all external images are
  13273. // loaded
  13274. ren.imgCount--;
  13275. if (!ren.imgCount && chart && !chart.hasLoaded) {
  13276. chart.onload();
  13277. }
  13278. },
  13279. src: imageSrc
  13280. });
  13281. this.imgCount++;
  13282. }
  13283. }
  13284. return obj;
  13285. }
  13286. /**
  13287. * Define a clipping rectangle. The clipping rectangle is later applied
  13288. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  13289. * function.
  13290. *
  13291. * @example
  13292. * let circle = renderer.circle(100, 100, 100)
  13293. * .attr({ fill: 'red' })
  13294. * .add();
  13295. * let clipRect = renderer.clipRect(100, 100, 100, 100);
  13296. *
  13297. * // Leave only the lower right quarter visible
  13298. * circle.clip(clipRect);
  13299. *
  13300. * @function Highcharts.SVGRenderer#clipRect
  13301. *
  13302. * @param {number} [x]
  13303. *
  13304. * @param {number} [y]
  13305. *
  13306. * @param {number} [width]
  13307. *
  13308. * @param {number} [height]
  13309. *
  13310. * @return {Highcharts.ClipRectElement}
  13311. * A clipping rectangle.
  13312. */
  13313. clipRect(x, y, width, height) {
  13314. const
  13315. // Add a hyphen at the end to avoid confusion in testing indexes
  13316. // -1 and -10, -11 etc (#6550)
  13317. id = uniqueKey() + '-', clipPath = this.createElement('clipPath').attr({
  13318. id: id
  13319. }).add(this.defs), wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  13320. wrapper.id = id;
  13321. wrapper.clipPath = clipPath;
  13322. wrapper.count = 0;
  13323. return wrapper;
  13324. }
  13325. /**
  13326. * Draw text. The text can contain a subset of HTML, like spans and anchors
  13327. * and some basic text styling of these. For more advanced features like
  13328. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  13329. * To update the text after render, run `text.attr({ text: 'New text' })`.
  13330. *
  13331. * @sample highcharts/members/renderer-text-on-chart/
  13332. * Annotate the chart freely
  13333. * @sample highcharts/members/renderer-on-chart/
  13334. * Annotate with a border and in response to the data
  13335. * @sample highcharts/members/renderer-text/
  13336. * Formatted text
  13337. *
  13338. * @function Highcharts.SVGRenderer#text
  13339. *
  13340. * @param {string} [str]
  13341. * The text of (subset) HTML to draw.
  13342. *
  13343. * @param {number} [x]
  13344. * The x position of the text's lower left corner.
  13345. *
  13346. * @param {number} [y]
  13347. * The y position of the text's lower left corner.
  13348. *
  13349. * @param {boolean} [useHTML=false]
  13350. * Use HTML to render the text.
  13351. *
  13352. * @return {Highcharts.SVGElement}
  13353. * The text object.
  13354. */
  13355. text(str, x, y, useHTML) {
  13356. const renderer = this, attribs = {};
  13357. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  13358. return renderer.html(str, x, y);
  13359. }
  13360. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  13361. if (y) {
  13362. attribs.y = Math.round(y);
  13363. }
  13364. if (defined(str)) {
  13365. attribs.text = str;
  13366. }
  13367. const wrapper = renderer.createElement('text').attr(attribs);
  13368. if (!useHTML || (renderer.forExport && !renderer.allowHTML)) {
  13369. wrapper.xSetter = function (value, key, element) {
  13370. const tspans = element.getElementsByTagName('tspan'), parentVal = element.getAttribute(key);
  13371. for (let i = 0, tspan; i < tspans.length; i++) {
  13372. tspan = tspans[i];
  13373. // If the x values are equal, the tspan represents a line
  13374. // break
  13375. if (tspan.getAttribute(key) === parentVal) {
  13376. tspan.setAttribute(key, value);
  13377. }
  13378. }
  13379. element.setAttribute(key, value);
  13380. };
  13381. }
  13382. return wrapper;
  13383. }
  13384. /**
  13385. * Utility to return the baseline offset and total line height from the font
  13386. * size.
  13387. *
  13388. * @function Highcharts.SVGRenderer#fontMetrics
  13389. *
  13390. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement|number} [element]
  13391. * The element to inspect for a current font size. If a number is
  13392. * given, it's used as a fall back for direct font size in pixels.
  13393. *
  13394. * @return {Highcharts.FontMetricsObject}
  13395. * The font metrics.
  13396. */
  13397. fontMetrics(element) {
  13398. const f = pInt(SVGElement.prototype.getStyle.call(element, 'font-size') || 0);
  13399. // Empirical values found by comparing font size and bounding box
  13400. // height. Applies to the default font family.
  13401. // https://jsfiddle.net/highcharts/7xvn7/
  13402. const h = f < 24 ? f + 3 : Math.round(f * 1.2), b = Math.round(h * 0.8);
  13403. return {
  13404. // Line height
  13405. h,
  13406. // Baseline
  13407. b,
  13408. // Font size
  13409. f
  13410. };
  13411. }
  13412. /**
  13413. * Correct X and Y positioning of a label for rotation (#1764).
  13414. *
  13415. * @private
  13416. * @function Highcharts.SVGRenderer#rotCorr
  13417. */
  13418. rotCorr(baseline, rotation, alterY) {
  13419. let y = baseline;
  13420. if (rotation && alterY) {
  13421. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  13422. }
  13423. return {
  13424. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  13425. y: y
  13426. };
  13427. }
  13428. /**
  13429. * Compatibility function to convert the legacy one-dimensional path array
  13430. * into an array of segments.
  13431. *
  13432. * It is used in maps to parse the `path` option, and in SVGRenderer.dSetter
  13433. * to support legacy paths from demos.
  13434. *
  13435. * @private
  13436. * @function Highcharts.SVGRenderer#pathToSegments
  13437. */
  13438. pathToSegments(path) {
  13439. const ret = [];
  13440. const segment = [];
  13441. const commandLength = {
  13442. A: 8,
  13443. C: 7,
  13444. H: 2,
  13445. L: 3,
  13446. M: 3,
  13447. Q: 5,
  13448. S: 5,
  13449. T: 3,
  13450. V: 2
  13451. };
  13452. // Short, non-typesafe parsing of the one-dimensional array. It splits
  13453. // the path on any string. This is not type checked against the tuple
  13454. // types, but is shorter, and doesn't require specific checks for any
  13455. // command type in SVG.
  13456. for (let i = 0; i < path.length; i++) {
  13457. // Command skipped, repeat previous or insert L/l for M/m
  13458. if (isString(segment[0]) &&
  13459. isNumber(path[i]) &&
  13460. segment.length === commandLength[(segment[0].toUpperCase())]) {
  13461. path.splice(i, 0, segment[0].replace('M', 'L').replace('m', 'l'));
  13462. }
  13463. // Split on string
  13464. if (typeof path[i] === 'string') {
  13465. if (segment.length) {
  13466. ret.push(segment.slice(0));
  13467. }
  13468. segment.length = 0;
  13469. }
  13470. segment.push(path[i]);
  13471. }
  13472. ret.push(segment.slice(0));
  13473. return ret;
  13474. /*
  13475. // Fully type-safe version where each tuple type is checked. The
  13476. // downside is filesize and a lack of flexibility for unsupported
  13477. // commands
  13478. const ret: SVGPath = [],
  13479. commands = {
  13480. A: 7,
  13481. C: 6,
  13482. H: 1,
  13483. L: 2,
  13484. M: 2,
  13485. Q: 4,
  13486. S: 4,
  13487. T: 2,
  13488. V: 1,
  13489. Z: 0
  13490. };
  13491. let i = 0,
  13492. lastI = 0,
  13493. lastCommand;
  13494. while (i < path.length) {
  13495. const item = path[i];
  13496. let command;
  13497. if (typeof item === 'string') {
  13498. command = item;
  13499. i += 1;
  13500. } else {
  13501. command = lastCommand || 'M';
  13502. }
  13503. // Upper case
  13504. const commandUC = command.toUpperCase();
  13505. if (commandUC in commands) {
  13506. // No numeric parameters
  13507. if (command === 'Z' || command === 'z') {
  13508. ret.push([command]);
  13509. // One numeric parameter
  13510. } else {
  13511. const val0 = path[i];
  13512. if (typeof val0 === 'number') {
  13513. // Horizontal line to
  13514. if (command === 'H' || command === 'h') {
  13515. ret.push([command, val0]);
  13516. i += 1;
  13517. // Vertical line to
  13518. } else if (command === 'V' || command === 'v') {
  13519. ret.push([command, val0]);
  13520. i += 1;
  13521. // Two numeric parameters
  13522. } else {
  13523. const val1 = path[i + 1];
  13524. if (typeof val1 === 'number') {
  13525. // lineTo
  13526. if (command === 'L' || command === 'l') {
  13527. ret.push([command, val0, val1]);
  13528. i += 2;
  13529. // moveTo
  13530. } else if (command === 'M' || command === 'm') {
  13531. ret.push([command, val0, val1]);
  13532. i += 2;
  13533. // Smooth quadratic bezier
  13534. } else if (command === 'T' || command === 't') {
  13535. ret.push([command, val0, val1]);
  13536. i += 2;
  13537. // Four numeric parameters
  13538. } else {
  13539. const val2 = path[i + 2],
  13540. val3 = path[i + 3];
  13541. if (
  13542. typeof val2 === 'number' &&
  13543. typeof val3 === 'number'
  13544. ) {
  13545. // Quadratic bezier to
  13546. if (
  13547. command === 'Q' ||
  13548. command === 'q'
  13549. ) {
  13550. ret.push([
  13551. command,
  13552. val0,
  13553. val1,
  13554. val2,
  13555. val3
  13556. ]);
  13557. i += 4;
  13558. // Smooth cubic bezier to
  13559. } else if (
  13560. command === 'S' ||
  13561. command === 's'
  13562. ) {
  13563. ret.push([
  13564. command,
  13565. val0,
  13566. val1,
  13567. val2,
  13568. val3
  13569. ]);
  13570. i += 4;
  13571. // Six numeric parameters
  13572. } else {
  13573. const val4 = path[i + 4],
  13574. val5 = path[i + 5];
  13575. if (
  13576. typeof val4 === 'number' &&
  13577. typeof val5 === 'number'
  13578. ) {
  13579. // Curve to
  13580. if (
  13581. command === 'C' ||
  13582. command === 'c'
  13583. ) {
  13584. ret.push([
  13585. command,
  13586. val0,
  13587. val1,
  13588. val2,
  13589. val3,
  13590. val4,
  13591. val5
  13592. ]);
  13593. i += 6;
  13594. // Seven numeric parameters
  13595. } else {
  13596. const val6 = path[i + 6];
  13597. // Arc to
  13598. if (
  13599. typeof val6 ===
  13600. 'number' &&
  13601. (
  13602. command === 'A' ||
  13603. command === 'a'
  13604. )
  13605. ) {
  13606. ret.push([
  13607. command,
  13608. val0,
  13609. val1,
  13610. val2,
  13611. val3,
  13612. val4,
  13613. val5,
  13614. val6
  13615. ]);
  13616. i += 7;
  13617. }
  13618. }
  13619. }
  13620. }
  13621. }
  13622. }
  13623. }
  13624. }
  13625. }
  13626. }
  13627. }
  13628. // An unmarked command following a moveTo is a lineTo
  13629. lastCommand = command === 'M' ? 'L' : command;
  13630. if (i === lastI) {
  13631. break;
  13632. }
  13633. lastI = i;
  13634. }
  13635. return ret;
  13636. */
  13637. }
  13638. /**
  13639. * Draw a label, which is an extended text element with support for border
  13640. * and background. Highcharts creates a `g` element with a text and a `path`
  13641. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  13642. * background are set through `stroke`, `stroke-width` and `fill` attributes
  13643. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  13644. * text after render, run `label.attr({ text: 'New text' })`.
  13645. *
  13646. * @sample highcharts/members/renderer-label-on-chart/
  13647. * A label on the chart
  13648. *
  13649. * @function Highcharts.SVGRenderer#label
  13650. *
  13651. * @param {string} str
  13652. * The initial text string or (subset) HTML to render.
  13653. *
  13654. * @param {number} x
  13655. * The x position of the label's left side.
  13656. *
  13657. * @param {number} [y]
  13658. * The y position of the label's top side or baseline, depending on
  13659. * the `baseline` parameter.
  13660. *
  13661. * @param {string} [shape='rect']
  13662. * The shape of the label's border/background, if any. Defaults to
  13663. * `rect`. Other possible values are `callout` or other shapes
  13664. * defined in {@link Highcharts.SVGRenderer#symbols}.
  13665. *
  13666. * @param {number} [anchorX]
  13667. * In case the `shape` has a pointer, like a flag, this is the
  13668. * coordinates it should be pinned to.
  13669. *
  13670. * @param {number} [anchorY]
  13671. * In case the `shape` has a pointer, like a flag, this is the
  13672. * coordinates it should be pinned to.
  13673. *
  13674. * @param {boolean} [useHTML=false]
  13675. * Whether to use HTML to render the label.
  13676. *
  13677. * @param {boolean} [baseline=false]
  13678. * Whether to position the label relative to the text baseline,
  13679. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  13680. * upper border of the rectangle.
  13681. *
  13682. * @param {string} [className]
  13683. * Class name for the group.
  13684. *
  13685. * @return {Highcharts.SVGElement}
  13686. * The generated label.
  13687. */
  13688. label(str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  13689. return new SVGLabel(this, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className);
  13690. }
  13691. /**
  13692. * Re-align all aligned elements.
  13693. *
  13694. * @private
  13695. * @function Highcharts.SVGRenderer#alignElements
  13696. */
  13697. alignElements() {
  13698. this.alignedObjects.forEach((el) => el.align());
  13699. }
  13700. }
  13701. extend(SVGRenderer.prototype, {
  13702. /**
  13703. * A pointer to the renderer's associated Element class.
  13704. *
  13705. * @name Highcharts.SVGRenderer#Element
  13706. * @type {Highcharts.SVGElement}
  13707. */
  13708. Element: SVGElement,
  13709. SVG_NS,
  13710. /**
  13711. * A collection of characters mapped to HTML entities. When `useHTML` on an
  13712. * element is true, these entities will be rendered correctly by HTML. In
  13713. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  13714. * so for example `&lt;` will render as `<`.
  13715. *
  13716. * @example
  13717. * // Add support for unescaping quotes
  13718. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  13719. *
  13720. * @name Highcharts.SVGRenderer#escapes
  13721. * @type {Highcharts.Dictionary<string>}
  13722. */
  13723. escapes: {
  13724. '&': '&amp;',
  13725. '<': '&lt;',
  13726. '>': '&gt;',
  13727. "'": '&#39;',
  13728. '"': '&quot;'
  13729. },
  13730. /**
  13731. * An extendable collection of functions for defining symbol paths.
  13732. *
  13733. * @name Highcharts.SVGRenderer#symbols
  13734. * @type {Highcharts.SymbolDictionary}
  13735. */
  13736. symbols: Symbols,
  13737. /**
  13738. * Dummy function for plugins, called every time the renderer is updated.
  13739. * Prior to Highcharts 5, this was used for the canvg renderer.
  13740. *
  13741. * @deprecated
  13742. * @function Highcharts.SVGRenderer#draw
  13743. */
  13744. draw: noop
  13745. });
  13746. /* *
  13747. *
  13748. * Registry
  13749. *
  13750. * */
  13751. RendererRegistry.registerRendererType('svg', SVGRenderer, true);
  13752. /* *
  13753. *
  13754. * Export Default
  13755. *
  13756. * */
  13757. /* *
  13758. *
  13759. * API Declarations
  13760. *
  13761. * */
  13762. /**
  13763. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  13764. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  13765. * and applied with the {@link SVGElement#clip} function.
  13766. *
  13767. * @example
  13768. * let circle = renderer.circle(100, 100, 100)
  13769. * .attr({ fill: 'red' })
  13770. * .add();
  13771. * let clipRect = renderer.clipRect(100, 100, 100, 100);
  13772. *
  13773. * // Leave only the lower right quarter visible
  13774. * circle.clip(clipRect);
  13775. *
  13776. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  13777. */
  13778. /**
  13779. * The font metrics.
  13780. *
  13781. * @interface Highcharts.FontMetricsObject
  13782. */ /**
  13783. * The baseline relative to the top of the box.
  13784. *
  13785. * @name Highcharts.FontMetricsObject#b
  13786. * @type {number}
  13787. */ /**
  13788. * The font size.
  13789. *
  13790. * @name Highcharts.FontMetricsObject#f
  13791. * @type {number}
  13792. */ /**
  13793. * The line height.
  13794. *
  13795. * @name Highcharts.FontMetricsObject#h
  13796. * @type {number}
  13797. */
  13798. /**
  13799. * An object containing `x` and `y` properties for the position of an element.
  13800. *
  13801. * @interface Highcharts.PositionObject
  13802. */ /**
  13803. * X position of the element.
  13804. * @name Highcharts.PositionObject#x
  13805. * @type {number}
  13806. */ /**
  13807. * Y position of the element.
  13808. * @name Highcharts.PositionObject#y
  13809. * @type {number}
  13810. */
  13811. /**
  13812. * A rectangle.
  13813. *
  13814. * @interface Highcharts.RectangleObject
  13815. */ /**
  13816. * Height of the rectangle.
  13817. * @name Highcharts.RectangleObject#height
  13818. * @type {number}
  13819. */ /**
  13820. * Width of the rectangle.
  13821. * @name Highcharts.RectangleObject#width
  13822. * @type {number}
  13823. */ /**
  13824. * Horizontal position of the rectangle.
  13825. * @name Highcharts.RectangleObject#x
  13826. * @type {number}
  13827. */ /**
  13828. * Vertical position of the rectangle.
  13829. * @name Highcharts.RectangleObject#y
  13830. * @type {number}
  13831. */
  13832. /**
  13833. * The shadow options.
  13834. *
  13835. * @interface Highcharts.ShadowOptionsObject
  13836. */ /**
  13837. * The shadow color.
  13838. * @name Highcharts.ShadowOptionsObject#color
  13839. * @type {Highcharts.ColorString|undefined}
  13840. * @default #000000
  13841. */ /**
  13842. * The horizontal offset from the element.
  13843. *
  13844. * @name Highcharts.ShadowOptionsObject#offsetX
  13845. * @type {number|undefined}
  13846. * @default 1
  13847. */ /**
  13848. * The vertical offset from the element.
  13849. * @name Highcharts.ShadowOptionsObject#offsetY
  13850. * @type {number|undefined}
  13851. * @default 1
  13852. */ /**
  13853. * The shadow opacity.
  13854. *
  13855. * @name Highcharts.ShadowOptionsObject#opacity
  13856. * @type {number|undefined}
  13857. * @default 0.15
  13858. */ /**
  13859. * The shadow width or distance from the element.
  13860. * @name Highcharts.ShadowOptionsObject#width
  13861. * @type {number|undefined}
  13862. * @default 3
  13863. */
  13864. /**
  13865. * @interface Highcharts.SizeObject
  13866. */ /**
  13867. * @name Highcharts.SizeObject#height
  13868. * @type {number}
  13869. */ /**
  13870. * @name Highcharts.SizeObject#width
  13871. * @type {number}
  13872. */
  13873. /**
  13874. * Array of path commands, that will go into the `d` attribute of an SVG
  13875. * element.
  13876. *
  13877. * @typedef {Array<(Array<Highcharts.SVGPathCommand>|Array<Highcharts.SVGPathCommand,number>|Array<Highcharts.SVGPathCommand,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number,number>)>} Highcharts.SVGPathArray
  13878. */
  13879. /**
  13880. * Possible path commands in an SVG path array. Valid values are `A`, `C`, `H`,
  13881. * `L`, `M`, `Q`, `S`, `T`, `V`, `Z`.
  13882. *
  13883. * @typedef {string} Highcharts.SVGPathCommand
  13884. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  13885. */
  13886. /**
  13887. * An extendable collection of functions for defining symbol paths. Symbols are
  13888. * used internally for point markers, button and label borders and backgrounds,
  13889. * or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
  13890. *
  13891. * @interface Highcharts.SymbolDictionary
  13892. */ /**
  13893. * @name Highcharts.SymbolDictionary#[key:string]
  13894. * @type {Function|undefined}
  13895. */ /**
  13896. * @name Highcharts.SymbolDictionary#arc
  13897. * @type {Function|undefined}
  13898. */ /**
  13899. * @name Highcharts.SymbolDictionary#callout
  13900. * @type {Function|undefined}
  13901. */ /**
  13902. * @name Highcharts.SymbolDictionary#circle
  13903. * @type {Function|undefined}
  13904. */ /**
  13905. * @name Highcharts.SymbolDictionary#diamond
  13906. * @type {Function|undefined}
  13907. */ /**
  13908. * @name Highcharts.SymbolDictionary#square
  13909. * @type {Function|undefined}
  13910. */ /**
  13911. * @name Highcharts.SymbolDictionary#triangle
  13912. * @type {Function|undefined}
  13913. */
  13914. /**
  13915. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
  13916. * and `triangle-down`. Symbols are used internally for point markers, button
  13917. * and label borders and backgrounds, or custom shapes. Extendable by adding to
  13918. * {@link SVGRenderer#symbols}.
  13919. *
  13920. * @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
  13921. */
  13922. /**
  13923. * Additional options, depending on the actual symbol drawn.
  13924. *
  13925. * @interface Highcharts.SymbolOptionsObject
  13926. */ /**
  13927. * The anchor X position for the `callout` symbol. This is where the chevron
  13928. * points to.
  13929. *
  13930. * @name Highcharts.SymbolOptionsObject#anchorX
  13931. * @type {number|undefined}
  13932. */ /**
  13933. * The anchor Y position for the `callout` symbol. This is where the chevron
  13934. * points to.
  13935. *
  13936. * @name Highcharts.SymbolOptionsObject#anchorY
  13937. * @type {number|undefined}
  13938. */ /**
  13939. * The end angle of an `arc` symbol.
  13940. *
  13941. * @name Highcharts.SymbolOptionsObject#end
  13942. * @type {number|undefined}
  13943. */ /**
  13944. * Whether to draw `arc` symbol open or closed.
  13945. *
  13946. * @name Highcharts.SymbolOptionsObject#open
  13947. * @type {boolean|undefined}
  13948. */ /**
  13949. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  13950. *
  13951. * @name Highcharts.SymbolOptionsObject#r
  13952. * @type {number|undefined}
  13953. */ /**
  13954. * The start angle of an `arc` symbol.
  13955. *
  13956. * @name Highcharts.SymbolOptionsObject#start
  13957. * @type {number|undefined}
  13958. */
  13959. (''); // keeps doclets above in transpiled file
  13960. return SVGRenderer;
  13961. });
  13962. _registerModule(_modules, 'Core/Renderer/HTML/HTMLElement.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (H, SVGElement, U) {
  13963. /* *
  13964. *
  13965. * (c) 2010-2021 Torstein Honsi
  13966. *
  13967. * License: www.highcharts.com/license
  13968. *
  13969. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  13970. *
  13971. * */
  13972. const { isFirefox, isMS, isWebKit, win } = H;
  13973. const { css, defined, extend, pick, pInt } = U;
  13974. /* *
  13975. *
  13976. * Constants
  13977. *
  13978. * */
  13979. const composedMembers = [];
  13980. /* *
  13981. *
  13982. * Class
  13983. *
  13984. * */
  13985. /* eslint-disable valid-jsdoc */
  13986. class HTMLElement extends SVGElement {
  13987. /* *
  13988. *
  13989. * Static Functions
  13990. *
  13991. * */
  13992. /**
  13993. * Modifies SVGElement to support HTML elements.
  13994. * @private
  13995. */
  13996. static compose(SVGElementClass) {
  13997. if (U.pushUnique(composedMembers, SVGElementClass)) {
  13998. const htmlElementProto = HTMLElement.prototype, svgElementProto = SVGElementClass.prototype;
  13999. svgElementProto.getSpanCorrection = htmlElementProto
  14000. .getSpanCorrection;
  14001. svgElementProto.htmlCss = htmlElementProto.htmlCss;
  14002. svgElementProto.htmlGetBBox = htmlElementProto.htmlGetBBox;
  14003. svgElementProto.htmlUpdateTransform = htmlElementProto
  14004. .htmlUpdateTransform;
  14005. svgElementProto.setSpanRotation = htmlElementProto.setSpanRotation;
  14006. }
  14007. return SVGElementClass;
  14008. }
  14009. /* *
  14010. *
  14011. * Functions
  14012. *
  14013. * */
  14014. /**
  14015. * Get the correction in X and Y positioning as the element is rotated.
  14016. * @private
  14017. */
  14018. getSpanCorrection(width, baseline, alignCorrection) {
  14019. this.xCorr = -width * alignCorrection;
  14020. this.yCorr = -baseline;
  14021. }
  14022. /**
  14023. * Apply CSS to HTML elements. This is used in text within SVG rendering.
  14024. * @private
  14025. */
  14026. htmlCss(styles) {
  14027. const wrapper = this, element = wrapper.element,
  14028. // When setting or unsetting the width style, we need to update
  14029. // transform (#8809)
  14030. isSettingWidth = (element.tagName === 'SPAN' &&
  14031. styles &&
  14032. 'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0);
  14033. let doTransform;
  14034. if (isSettingWidth) {
  14035. delete styles.width;
  14036. wrapper.textWidth = textWidth;
  14037. doTransform = true;
  14038. }
  14039. if (styles && styles.textOverflow === 'ellipsis') {
  14040. styles.whiteSpace = 'nowrap';
  14041. styles.overflow = 'hidden';
  14042. }
  14043. wrapper.styles = extend(wrapper.styles, styles);
  14044. css(wrapper.element, styles);
  14045. // Now that all styles are applied, to the transform
  14046. if (doTransform) {
  14047. wrapper.htmlUpdateTransform();
  14048. }
  14049. return wrapper;
  14050. }
  14051. /**
  14052. * useHTML method for calculating the bounding box based on offsets.
  14053. */
  14054. htmlGetBBox() {
  14055. const wrapper = this, element = wrapper.element;
  14056. return {
  14057. x: element.offsetLeft,
  14058. y: element.offsetTop,
  14059. width: element.offsetWidth,
  14060. height: element.offsetHeight
  14061. };
  14062. }
  14063. /**
  14064. * @private
  14065. */
  14066. htmlUpdateTransform() {
  14067. // aligning non added elements is expensive
  14068. if (!this.added) {
  14069. this.alignOnAdd = true;
  14070. return;
  14071. }
  14072. const wrapper = this, renderer = wrapper.renderer, elem = wrapper.element, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, x = wrapper.x || 0, y = wrapper.y || 0, align = wrapper.textAlign || 'left', alignCorrection = {
  14073. left: 0, center: 0.5, right: 1
  14074. }[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
  14075. /** @private */
  14076. function getTextPxLength() {
  14077. if (wrapper.textPxLength) {
  14078. return wrapper.textPxLength;
  14079. }
  14080. // Reset multiline/ellipsis in order to read width (#4928,
  14081. // #5417)
  14082. css(elem, {
  14083. width: '',
  14084. whiteSpace: whiteSpace || 'nowrap'
  14085. });
  14086. return elem.offsetWidth;
  14087. }
  14088. // apply translate
  14089. css(elem, {
  14090. marginLeft: translateX,
  14091. marginTop: translateY
  14092. });
  14093. if (elem.tagName === 'SPAN') {
  14094. const rotation = wrapper.rotation, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
  14095. rotation,
  14096. align,
  14097. elem.innerHTML,
  14098. wrapper.textWidth,
  14099. wrapper.textAlign
  14100. ].join(',');
  14101. let baseline, hasBoxWidthChanged = false;
  14102. // Update textWidth. Use the memoized textPxLength if possible, to
  14103. // avoid the getTextPxLength function using elem.offsetWidth.
  14104. // Calling offsetWidth affects rendering time as it forces layout
  14105. // (#7656).
  14106. if (textWidth !== wrapper.oldTextWidth) { // #983, #1254
  14107. const textPxLength = getTextPxLength();
  14108. if (((textWidth > wrapper.oldTextWidth) ||
  14109. textPxLength > textWidth) && (
  14110. // Only set the width if the text is able to word-wrap,
  14111. // or text-overflow is ellipsis (#9537)
  14112. /[ \-]/.test(elem.textContent || elem.innerText) ||
  14113. elem.style.textOverflow === 'ellipsis')) {
  14114. css(elem, {
  14115. width: (textPxLength > textWidth) || rotation ?
  14116. textWidth + 'px' :
  14117. 'auto',
  14118. display: 'block',
  14119. whiteSpace: whiteSpace || 'normal' // #3331
  14120. });
  14121. wrapper.oldTextWidth = textWidth;
  14122. hasBoxWidthChanged = true; // #8159
  14123. }
  14124. }
  14125. wrapper.hasBoxWidthChanged = hasBoxWidthChanged; // #8159
  14126. // Do the calculations and DOM access only if properties changed
  14127. if (currentTextTransform !== wrapper.cTT) {
  14128. baseline = renderer.fontMetrics(elem).b;
  14129. // Renderer specific handling of span rotation, but only if we
  14130. // have something to update.
  14131. if (defined(rotation) &&
  14132. ((rotation !== (wrapper.oldRotation || 0)) ||
  14133. (align !== wrapper.oldAlign))) {
  14134. wrapper.setSpanRotation(rotation, alignCorrection, baseline);
  14135. }
  14136. wrapper.getSpanCorrection(
  14137. // Avoid elem.offsetWidth if we can, it affects rendering
  14138. // time heavily (#7656)
  14139. ((!defined(rotation) && wrapper.textPxLength) || // #7920
  14140. elem.offsetWidth), baseline, alignCorrection, rotation, align);
  14141. }
  14142. // apply position with correction
  14143. css(elem, {
  14144. left: (x + (wrapper.xCorr || 0)) + 'px',
  14145. top: (y + (wrapper.yCorr || 0)) + 'px'
  14146. });
  14147. // record current text transform
  14148. wrapper.cTT = currentTextTransform;
  14149. wrapper.oldRotation = rotation;
  14150. wrapper.oldAlign = align;
  14151. }
  14152. }
  14153. /**
  14154. * Set the rotation of an individual HTML span.
  14155. * @private
  14156. */
  14157. setSpanRotation(rotation, alignCorrection, baseline) {
  14158. const getTransformKey = () => (isMS &&
  14159. !/Edge/.test(win.navigator.userAgent) ?
  14160. '-ms-transform' :
  14161. isWebKit ?
  14162. '-webkit-transform' :
  14163. isFirefox ?
  14164. 'MozTransform' :
  14165. win.opera ?
  14166. '-o-transform' :
  14167. void 0);
  14168. const rotationStyle = {}, cssTransformKey = getTransformKey();
  14169. if (cssTransformKey) {
  14170. rotationStyle[cssTransformKey] = rotationStyle.transform =
  14171. 'rotate(' + rotation + 'deg)';
  14172. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] = rotationStyle.transformOrigin =
  14173. (alignCorrection * 100) + '% ' + baseline + 'px';
  14174. css(this.element, rotationStyle);
  14175. }
  14176. }
  14177. }
  14178. /* *
  14179. *
  14180. * Default Export
  14181. *
  14182. * */
  14183. return HTMLElement;
  14184. });
  14185. _registerModule(_modules, 'Core/Renderer/HTML/HTMLRenderer.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (AST, SVGElement, SVGRenderer, U) {
  14186. /* *
  14187. *
  14188. * (c) 2010-2021 Torstein Honsi
  14189. *
  14190. * License: www.highcharts.com/license
  14191. *
  14192. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  14193. *
  14194. * */
  14195. const { attr, createElement, extend, pick } = U;
  14196. /* *
  14197. *
  14198. * Constants
  14199. *
  14200. * */
  14201. const composedMembers = [];
  14202. /* *
  14203. *
  14204. * Class
  14205. *
  14206. * */
  14207. /* eslint-disable valid-jsdoc */
  14208. // Extend SvgRenderer for useHTML option.
  14209. class HTMLRenderer extends SVGRenderer {
  14210. /* *
  14211. *
  14212. * Static Functions
  14213. *
  14214. * */
  14215. /** @private */
  14216. static compose(SVGRendererClass) {
  14217. if (U.pushUnique(composedMembers, SVGRendererClass)) {
  14218. const htmlRendererProto = HTMLRenderer.prototype, svgRendererProto = SVGRendererClass.prototype;
  14219. svgRendererProto.html = htmlRendererProto.html;
  14220. }
  14221. return SVGRendererClass;
  14222. }
  14223. /* *
  14224. *
  14225. * Functions
  14226. *
  14227. * */
  14228. /**
  14229. * Create HTML text node. This is used by the SVG renderer through the
  14230. * useHTML option.
  14231. *
  14232. * @private
  14233. * @function Highcharts.SVGRenderer#html
  14234. *
  14235. * @param {string} str
  14236. * The text of (subset) HTML to draw.
  14237. *
  14238. * @param {number} x
  14239. * The x position of the text's lower left corner.
  14240. *
  14241. * @param {number} y
  14242. * The y position of the text's lower left corner.
  14243. *
  14244. * @return {Highcharts.HTMLDOMElement}
  14245. * HTML element.
  14246. */
  14247. html(str, x, y) {
  14248. const wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, addSetters = function (gWrapper, style) {
  14249. // These properties are set as attributes on the SVG group, and
  14250. // as identical CSS properties on the div. (#3542)
  14251. ['opacity', 'visibility'].forEach(function (prop) {
  14252. gWrapper[prop + 'Setter'] = function (value, key, elem) {
  14253. const styleObject = gWrapper.div ?
  14254. gWrapper.div.style :
  14255. style;
  14256. SVGElement.prototype[prop + 'Setter']
  14257. .call(this, value, key, elem);
  14258. if (styleObject) {
  14259. styleObject[key] = value;
  14260. }
  14261. };
  14262. });
  14263. gWrapper.addedSetters = true;
  14264. };
  14265. // Text setter
  14266. wrapper.textSetter = function (value) {
  14267. if (value !== this.textStr) {
  14268. delete this.bBox;
  14269. delete this.oldTextWidth;
  14270. AST.setElementHTML(this.element, pick(value, ''));
  14271. this.textStr = value;
  14272. wrapper.doTransform = true;
  14273. }
  14274. };
  14275. addSetters(wrapper, wrapper.element.style);
  14276. // Various setters which rely on update transform
  14277. wrapper.xSetter =
  14278. wrapper.ySetter =
  14279. wrapper.alignSetter =
  14280. wrapper.rotationSetter =
  14281. function (value, key) {
  14282. if (key === 'align') {
  14283. // Do not overwrite the SVGElement.align method.
  14284. wrapper.alignValue = wrapper.textAlign = value;
  14285. }
  14286. else {
  14287. wrapper[key] = value;
  14288. }
  14289. wrapper.doTransform = true;
  14290. };
  14291. // Runs at the end of .attr()
  14292. wrapper.afterSetters = function () {
  14293. // Update transform. Do this outside the loop to prevent redundant
  14294. // updating for batch setting of attributes.
  14295. if (this.doTransform) {
  14296. this.htmlUpdateTransform();
  14297. this.doTransform = false;
  14298. }
  14299. };
  14300. // Set the default attributes
  14301. wrapper
  14302. .attr({
  14303. text: str,
  14304. x: Math.round(x),
  14305. y: Math.round(y)
  14306. })
  14307. .css({
  14308. position: 'absolute'
  14309. });
  14310. if (!renderer.styledMode) {
  14311. wrapper.css({
  14312. fontFamily: this.style.fontFamily,
  14313. fontSize: this.style.fontSize
  14314. });
  14315. }
  14316. // Keep the whiteSpace style outside the wrapper.styles collection
  14317. element.style.whiteSpace = 'nowrap';
  14318. // Use the HTML specific .css method
  14319. wrapper.css = wrapper.htmlCss;
  14320. wrapper.add = function (svgGroupWrapper) {
  14321. const container = renderer.box.parentNode, parents = [];
  14322. let htmlGroup, parentGroup;
  14323. this.parentGroup = svgGroupWrapper;
  14324. // Create a mock group to hold the HTML elements
  14325. if (svgGroupWrapper) {
  14326. htmlGroup = svgGroupWrapper.div;
  14327. if (!htmlGroup) {
  14328. // Read the parent chain into an array and read from top
  14329. // down
  14330. parentGroup = svgGroupWrapper;
  14331. while (parentGroup) {
  14332. parents.push(parentGroup);
  14333. // Move up to the next parent group
  14334. parentGroup = parentGroup.parentGroup;
  14335. }
  14336. // Ensure dynamically updating position when any parent
  14337. // is translated
  14338. parents.reverse().forEach(function (parentGroup) {
  14339. const cls = attr(parentGroup.element, 'class');
  14340. /**
  14341. * Common translate setter for X and Y on the HTML
  14342. * group. Reverted the fix for #6957 du to
  14343. * positioning problems and offline export (#7254,
  14344. * #7280, #7529)
  14345. * @private
  14346. * @param {*} value
  14347. * @param {string} key
  14348. */
  14349. function translateSetter(value, key) {
  14350. parentGroup[key] = value;
  14351. if (key === 'translateX') {
  14352. htmlGroupStyle.left = value + 'px';
  14353. }
  14354. else {
  14355. htmlGroupStyle.top = value + 'px';
  14356. }
  14357. parentGroup.doTransform = true;
  14358. }
  14359. // Create a HTML div and append it to the parent div
  14360. // to emulate the SVG group structure
  14361. const parentGroupStyles = parentGroup.styles || {};
  14362. htmlGroup =
  14363. parentGroup.div =
  14364. parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
  14365. position: 'absolute',
  14366. left: (parentGroup.translateX || 0) + 'px',
  14367. top: (parentGroup.translateY || 0) + 'px',
  14368. display: parentGroup.display,
  14369. opacity: parentGroup.opacity,
  14370. visibility: parentGroup.visibility
  14371. // the top group is appended to container
  14372. }, htmlGroup || container);
  14373. // Shortcut
  14374. const htmlGroupStyle = htmlGroup.style;
  14375. // Set listeners to update the HTML div's position
  14376. // whenever the SVG group position is changed.
  14377. extend(parentGroup, {
  14378. // (#7287) Pass htmlGroup to use
  14379. // the related group
  14380. classSetter: (function (htmlGroup) {
  14381. return function (value) {
  14382. this.element.setAttribute('class', value);
  14383. htmlGroup.className = value;
  14384. };
  14385. }(htmlGroup)),
  14386. // Extend the parent group's css function by
  14387. // updating the shadow div counterpart with the same
  14388. // style.
  14389. css: function (styles) {
  14390. wrapper.css.call(parentGroup, styles);
  14391. [
  14392. // #6794
  14393. 'cursor',
  14394. // #5595, #18821
  14395. 'pointerEvents'
  14396. ].forEach((prop) => {
  14397. if (styles[prop]) {
  14398. htmlGroupStyle[prop] = styles[prop];
  14399. }
  14400. });
  14401. return parentGroup;
  14402. },
  14403. on: function () {
  14404. if (parents[0].div) { // #6418
  14405. wrapper.on.apply({
  14406. element: parents[0].div,
  14407. onEvents: parentGroup.onEvents
  14408. }, arguments);
  14409. }
  14410. return parentGroup;
  14411. },
  14412. translateXSetter: translateSetter,
  14413. translateYSetter: translateSetter
  14414. });
  14415. if (!parentGroup.addedSetters) {
  14416. addSetters(parentGroup);
  14417. }
  14418. // Apply pre-existing style
  14419. parentGroup.css(parentGroupStyles);
  14420. });
  14421. }
  14422. }
  14423. else {
  14424. htmlGroup = container;
  14425. }
  14426. htmlGroup.appendChild(element);
  14427. wrapper.added = true;
  14428. if (wrapper.alignOnAdd) {
  14429. wrapper.htmlUpdateTransform();
  14430. }
  14431. return wrapper;
  14432. };
  14433. return wrapper;
  14434. }
  14435. }
  14436. /* *
  14437. *
  14438. * Default Export
  14439. *
  14440. * */
  14441. return HTMLRenderer;
  14442. });
  14443. _registerModule(_modules, 'Core/Axis/AxisDefaults.js', [], function () {
  14444. /* *
  14445. *
  14446. * (c) 2010-2021 Torstein Honsi
  14447. *
  14448. * License: www.highcharts.com/license
  14449. *
  14450. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  14451. *
  14452. * */
  14453. /* *
  14454. *
  14455. * Namespace
  14456. *
  14457. * */
  14458. var AxisDefaults;
  14459. (function (AxisDefaults) {
  14460. /* *
  14461. *
  14462. * Constants
  14463. *
  14464. * */
  14465. /**
  14466. * The X axis or category axis. Normally this is the horizontal axis,
  14467. * though if the chart is inverted this is the vertical axis. In case of
  14468. * multiple axes, the xAxis node is an array of configuration objects.
  14469. *
  14470. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  14471. * access to the axis.
  14472. *
  14473. * @productdesc {highmaps}
  14474. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  14475. * control features like zooming and panning. Zooming is in effect the same
  14476. * as setting the extremes of one of the exes.
  14477. *
  14478. * @type {*|Array<*>}
  14479. * @optionparent xAxis
  14480. */
  14481. AxisDefaults.defaultXAxisOptions = {
  14482. /**
  14483. * When using multiple axis, the ticks of two or more opposite axes
  14484. * will automatically be aligned by adding ticks to the axis or axes
  14485. * with the least ticks, as if `tickAmount` were specified.
  14486. *
  14487. * This can be prevented by setting `alignTicks` to false. If the grid
  14488. * lines look messy, it's a good idea to hide them for the secondary
  14489. * axis by setting `gridLineWidth` to 0.
  14490. *
  14491. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  14492. * then the `alignTicks ` will be disabled for the Axis.
  14493. *
  14494. * Disabled for logarithmic axes.
  14495. *
  14496. * @product highcharts highstock gantt
  14497. */
  14498. alignTicks: true,
  14499. /**
  14500. * Whether to allow decimals in this axis' ticks. When counting
  14501. * integers, like persons or hits on a web page, decimals should
  14502. * be avoided in the labels. By default, decimals are allowed on small
  14503. * scale axes.
  14504. *
  14505. * @see [minTickInterval](#xAxis.minTickInterval)
  14506. *
  14507. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  14508. * True by default
  14509. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  14510. * False
  14511. *
  14512. * @type {boolean|undefined}
  14513. * @default undefined
  14514. * @since 2.0
  14515. */
  14516. allowDecimals: void 0,
  14517. /**
  14518. * When using an alternate grid color, a band is painted across the
  14519. * plot area between every other grid line.
  14520. *
  14521. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  14522. * Alternate grid color on the Y axis
  14523. * @sample {highstock} stock/xaxis/alternategridcolor/
  14524. * Alternate grid color on the Y axis
  14525. *
  14526. * @type {Highcharts.ColorType}
  14527. * @apioption xAxis.alternateGridColor
  14528. */
  14529. /**
  14530. * An array defining breaks in the axis, the sections defined will be
  14531. * left out and all the points shifted closer to each other.
  14532. *
  14533. * @productdesc {highcharts}
  14534. * Requires that the broken-axis.js module is loaded.
  14535. *
  14536. * @sample {highcharts} highcharts/axisbreak/break-simple/
  14537. * Simple break
  14538. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  14539. * Advanced with callback
  14540. * @sample {highstock} stock/demo/intraday-breaks/
  14541. * Break on nights and weekends
  14542. *
  14543. * @type {Array<*>}
  14544. * @since 4.1.0
  14545. * @product highcharts highstock gantt
  14546. * @apioption xAxis.breaks
  14547. */
  14548. /**
  14549. * A number indicating how much space should be left between the start
  14550. * and the end of the break. The break size is given in axis units,
  14551. * so for instance on a `datetime` axis, a break size of 3600000 would
  14552. * indicate the equivalent of an hour.
  14553. *
  14554. * @type {number}
  14555. * @default 0
  14556. * @since 4.1.0
  14557. * @product highcharts highstock gantt
  14558. * @apioption xAxis.breaks.breakSize
  14559. */
  14560. /**
  14561. * The point where the break starts.
  14562. *
  14563. * @type {number}
  14564. * @since 4.1.0
  14565. * @product highcharts highstock gantt
  14566. * @apioption xAxis.breaks.from
  14567. */
  14568. /**
  14569. * Defines an interval after which the break appears again. By default
  14570. * the breaks do not repeat.
  14571. *
  14572. * @type {number}
  14573. * @default 0
  14574. * @since 4.1.0
  14575. * @product highcharts highstock gantt
  14576. * @apioption xAxis.breaks.repeat
  14577. */
  14578. /**
  14579. * The point where the break ends.
  14580. *
  14581. * @type {number}
  14582. * @since 4.1.0
  14583. * @product highcharts highstock gantt
  14584. * @apioption xAxis.breaks.to
  14585. */
  14586. /**
  14587. * If categories are present for the xAxis, names are used instead of
  14588. * numbers for that axis.
  14589. *
  14590. * Since Highcharts 3.0, categories can also
  14591. * be extracted by giving each point a [name](#series.data) and setting
  14592. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  14593. * series, best practice remains defining the `categories` array.
  14594. *
  14595. * Example: `categories: ['Apples', 'Bananas', 'Oranges']`
  14596. *
  14597. * @sample {highcharts} highcharts/demo/line-labels/
  14598. * With
  14599. * @sample {highcharts} highcharts/xaxis/categories/
  14600. * Without
  14601. *
  14602. * @type {Array<string>}
  14603. * @product highcharts gantt
  14604. * @apioption xAxis.categories
  14605. */
  14606. /**
  14607. * The highest allowed value for automatically computed axis extremes.
  14608. *
  14609. * @see [floor](#xAxis.floor)
  14610. *
  14611. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  14612. * Floor and ceiling
  14613. *
  14614. * @type {number}
  14615. * @since 4.0
  14616. * @product highcharts highstock gantt
  14617. * @apioption xAxis.ceiling
  14618. */
  14619. /**
  14620. * A class name that opens for styling the axis by CSS, especially in
  14621. * Highcharts styled mode. The class name is applied to group elements
  14622. * for the grid, axis elements and labels.
  14623. *
  14624. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  14625. * Multiple axes with separate styling
  14626. *
  14627. * @type {string}
  14628. * @since 5.0.0
  14629. * @apioption xAxis.className
  14630. */
  14631. /**
  14632. * Configure a crosshair that follows either the mouse pointer or the
  14633. * hovered point.
  14634. *
  14635. * In styled mode, the crosshairs are styled in the
  14636. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  14637. * `.highcharts-xaxis-category` classes.
  14638. *
  14639. * @productdesc {highstock}
  14640. * In Highcharts stock, by default, the crosshair is enabled on the
  14641. * X axis and disabled on the Y axis.
  14642. *
  14643. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  14644. * Crosshair on both axes
  14645. * @sample {highstock} stock/xaxis/crosshairs-xy/
  14646. * Crosshair on both axes, with y axis label
  14647. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  14648. * Crosshair on both axes
  14649. *
  14650. * @declare Highcharts.AxisCrosshairOptions
  14651. * @type {boolean|*}
  14652. * @default false
  14653. * @since 4.1
  14654. * @apioption xAxis.crosshair
  14655. */
  14656. /**
  14657. * The value on a perpendicular axis where this axis should cross. This
  14658. * is typically used on mathematical plots where the axes cross at 0.
  14659. * When `crossing` is set, space will not be reserved at the sides of
  14660. * the chart for axis labels and title, so those may be clipped. In this
  14661. * case it is better to place the axes without the `crossing` option.
  14662. *
  14663. * @type {number}
  14664. * @sample highcharts/xaxis/crossing
  14665. * Function plot with axes crossing at 0
  14666. * @since 11.0.1
  14667. * @apioption xAxis.crossing
  14668. */
  14669. /**
  14670. * A class name for the crosshair, especially as a hook for styling.
  14671. *
  14672. * @type {string}
  14673. * @since 5.0.0
  14674. * @apioption xAxis.crosshair.className
  14675. */
  14676. /**
  14677. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  14678. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  14679. * the crosshair by default highlights the whole category.
  14680. *
  14681. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  14682. * Customized crosshairs
  14683. *
  14684. * @type {Highcharts.ColorType}
  14685. * @default #cccccc
  14686. * @since 4.1
  14687. * @apioption xAxis.crosshair.color
  14688. */
  14689. /**
  14690. * The dash style for the crosshair. See
  14691. * [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  14692. * for possible values.
  14693. *
  14694. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  14695. * Dotted crosshair
  14696. * @sample {highstock} stock/xaxis/crosshair-dashed/
  14697. * Dashed X axis crosshair
  14698. *
  14699. * @type {Highcharts.DashStyleValue}
  14700. * @default Solid
  14701. * @since 4.1
  14702. * @apioption xAxis.crosshair.dashStyle
  14703. */
  14704. /**
  14705. * A label on the axis next to the crosshair.
  14706. *
  14707. * In styled mode, the label is styled with the
  14708. * `.highcharts-crosshair-label` class.
  14709. *
  14710. * @sample {highstock} stock/xaxis/crosshair-label/
  14711. * Crosshair labels
  14712. * @sample {highstock} highcharts/css/crosshair-label/
  14713. * Style mode
  14714. *
  14715. * @declare Highcharts.AxisCrosshairLabelOptions
  14716. * @since 2.1
  14717. * @product highstock
  14718. * @apioption xAxis.crosshair.label
  14719. */
  14720. /**
  14721. * Alignment of the label compared to the axis. Defaults to `"left"` for
  14722. * right-side axes, `"right"` for left-side axes and `"center"` for
  14723. * horizontal axes.
  14724. *
  14725. * @type {Highcharts.AlignValue}
  14726. * @since 2.1
  14727. * @product highstock
  14728. * @apioption xAxis.crosshair.label.align
  14729. */
  14730. /**
  14731. * The background color for the label. Defaults to the related series
  14732. * color, or `#666666` if that is not available.
  14733. *
  14734. * @type {Highcharts.ColorType}
  14735. * @since 2.1
  14736. * @product highstock
  14737. * @apioption xAxis.crosshair.label.backgroundColor
  14738. */
  14739. /**
  14740. * The border color for the crosshair label
  14741. *
  14742. * @type {Highcharts.ColorType}
  14743. * @since 2.1
  14744. * @product highstock
  14745. * @apioption xAxis.crosshair.label.borderColor
  14746. */
  14747. /**
  14748. * The border corner radius of the crosshair label.
  14749. *
  14750. * @type {number}
  14751. * @default 3
  14752. * @since 2.1.10
  14753. * @product highstock
  14754. * @apioption xAxis.crosshair.label.borderRadius
  14755. */
  14756. /**
  14757. * The border width for the crosshair label.
  14758. *
  14759. * @type {number}
  14760. * @default 0
  14761. * @since 2.1
  14762. * @product highstock
  14763. * @apioption xAxis.crosshair.label.borderWidth
  14764. */
  14765. /**
  14766. * Flag to enable crosshair's label.
  14767. *
  14768. * @sample {highstock} stock/xaxis/crosshairs-xy/
  14769. * Enabled label for yAxis' crosshair
  14770. *
  14771. * @type {boolean}
  14772. * @default false
  14773. * @since 2.1
  14774. * @product highstock
  14775. * @apioption xAxis.crosshair.label.enabled
  14776. */
  14777. /**
  14778. * A format string for the crosshair label. Defaults to `{value}` for
  14779. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  14780. *
  14781. * @type {string}
  14782. * @since 2.1
  14783. * @product highstock
  14784. * @apioption xAxis.crosshair.label.format
  14785. */
  14786. /**
  14787. * Formatter function for the label text.
  14788. *
  14789. * @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
  14790. * @since 2.1
  14791. * @product highstock
  14792. * @apioption xAxis.crosshair.label.formatter
  14793. */
  14794. /**
  14795. * Padding inside the crosshair label.
  14796. *
  14797. * @type {number}
  14798. * @default 8
  14799. * @since 2.1
  14800. * @product highstock
  14801. * @apioption xAxis.crosshair.label.padding
  14802. */
  14803. /**
  14804. * The shape to use for the label box.
  14805. *
  14806. * @type {string}
  14807. * @default callout
  14808. * @since 2.1
  14809. * @product highstock
  14810. * @apioption xAxis.crosshair.label.shape
  14811. */
  14812. /**
  14813. * Text styles for the crosshair label.
  14814. *
  14815. * @type {Highcharts.CSSObject}
  14816. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  14817. * @since 2.1
  14818. * @product highstock
  14819. * @apioption xAxis.crosshair.label.style
  14820. */
  14821. /**
  14822. * Whether the crosshair should snap to the point or follow the pointer
  14823. * independent of points.
  14824. *
  14825. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  14826. * True by default
  14827. * @sample {highmaps} maps/demo/latlon-advanced/
  14828. * Snap is false
  14829. *
  14830. * @type {boolean}
  14831. * @default true
  14832. * @since 4.1
  14833. * @apioption xAxis.crosshair.snap
  14834. */
  14835. /**
  14836. * The pixel width of the crosshair. Defaults to 1 for numeric or
  14837. * datetime axes, and for one category width for category axes.
  14838. *
  14839. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  14840. * Customized crosshairs
  14841. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  14842. * Customized crosshairs
  14843. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  14844. * Customized crosshairs
  14845. *
  14846. * @type {number}
  14847. * @default 1
  14848. * @since 4.1
  14849. * @apioption xAxis.crosshair.width
  14850. */
  14851. /**
  14852. * The Z index of the crosshair. Higher Z indices allow drawing the
  14853. * crosshair on top of the series or behind the grid lines.
  14854. *
  14855. * @type {number}
  14856. * @default 2
  14857. * @since 4.1
  14858. * @apioption xAxis.crosshair.zIndex
  14859. */
  14860. /**
  14861. * Whether to pan axis. If `chart.panning` is enabled, the option
  14862. * allows to disable panning on an individual axis.
  14863. */
  14864. panningEnabled: true,
  14865. /**
  14866. * The Z index for the axis group.
  14867. */
  14868. zIndex: 2,
  14869. /**
  14870. * Whether to zoom axis. If `chart.zoomType` is set, the option allows
  14871. * to disable zooming on an individual axis.
  14872. *
  14873. * @sample {highcharts} highcharts/xaxis/zoomenabled/
  14874. * Zoom enabled is false
  14875. */
  14876. zoomEnabled: true,
  14877. /**
  14878. * For a datetime axis, the scale will automatically adjust to the
  14879. * appropriate unit. This member gives the default string
  14880. * representations used for each unit. For intermediate values,
  14881. * different units may be used, for example the `day` unit can be used
  14882. * on midnight and `hour` unit be used for intermediate values on the
  14883. * same axis.
  14884. *
  14885. * For an overview of the replacement codes, see
  14886. * [dateFormat](/class-reference/Highcharts.Time#dateFormat).
  14887. *
  14888. * Defaults to:
  14889. * ```js
  14890. * {
  14891. * millisecond: '%H:%M:%S.%L',
  14892. * second: '%H:%M:%S',
  14893. * minute: '%H:%M',
  14894. * hour: '%H:%M',
  14895. * day: '%e. %b',
  14896. * week: '%e. %b',
  14897. * month: '%b \'%y',
  14898. * year: '%Y'
  14899. * }
  14900. * ```
  14901. *
  14902. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  14903. * Different day format on X axis
  14904. * @sample {highstock} stock/xaxis/datetimelabelformats/
  14905. * More information in x axis labels
  14906. *
  14907. * @declare Highcharts.AxisDateTimeLabelFormatsOptions
  14908. * @product highcharts highstock gantt
  14909. */
  14910. dateTimeLabelFormats: {
  14911. /**
  14912. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14913. * @type {string|*}
  14914. */
  14915. millisecond: {
  14916. main: '%H:%M:%S.%L',
  14917. range: false
  14918. },
  14919. /**
  14920. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14921. * @type {string|*}
  14922. */
  14923. second: {
  14924. main: '%H:%M:%S',
  14925. range: false
  14926. },
  14927. /**
  14928. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14929. * @type {string|*}
  14930. */
  14931. minute: {
  14932. main: '%H:%M',
  14933. range: false
  14934. },
  14935. /**
  14936. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14937. * @type {string|*}
  14938. */
  14939. hour: {
  14940. main: '%H:%M',
  14941. range: false
  14942. },
  14943. /**
  14944. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14945. * @type {string|*}
  14946. */
  14947. day: {
  14948. main: '%e %b'
  14949. },
  14950. /**
  14951. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14952. * @type {string|*}
  14953. */
  14954. week: {
  14955. main: '%e %b'
  14956. },
  14957. /**
  14958. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14959. * @type {string|*}
  14960. */
  14961. month: {
  14962. main: '%b \'%y'
  14963. },
  14964. /**
  14965. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14966. * @type {string|*}
  14967. */
  14968. year: {
  14969. main: '%Y'
  14970. }
  14971. },
  14972. /**
  14973. * Whether to force the axis to end on a tick. Use this option with
  14974. * the `maxPadding` option to control the axis end.
  14975. *
  14976. * @productdesc {highstock}
  14977. * In Highcharts Stock, `endOnTick` is always `false` when the navigator
  14978. * is enabled, to prevent jumpy scrolling.
  14979. *
  14980. * @sample {highcharts} highcharts/yaxis/endontick/
  14981. * True by default
  14982. * @sample {highcharts} highcharts/yaxis/endontick-false/
  14983. * False
  14984. * @sample {highstock} stock/demo/basic-line/
  14985. * True by default
  14986. * @sample {highstock} stock/xaxis/endontick/
  14987. * False
  14988. *
  14989. * @since 1.2.0
  14990. */
  14991. endOnTick: false,
  14992. /**
  14993. * Event handlers for the axis.
  14994. *
  14995. * @type {*}
  14996. * @apioption xAxis.events
  14997. */
  14998. /**
  14999. * An event fired after the breaks have rendered.
  15000. *
  15001. * @see [breaks](#xAxis.breaks)
  15002. *
  15003. * @sample {highcharts} highcharts/axisbreak/break-event/
  15004. * AfterBreak Event
  15005. *
  15006. * @type {Highcharts.AxisEventCallbackFunction}
  15007. * @since 4.1.0
  15008. * @product highcharts gantt
  15009. * @apioption xAxis.events.afterBreaks
  15010. */
  15011. /**
  15012. * As opposed to the `setExtremes` event, this event fires after the
  15013. * final min and max values are computed and corrected for `minRange`.
  15014. *
  15015. * Fires when the minimum and maximum is set for the axis, either by
  15016. * calling the `.setExtremes()` method or by selecting an area in the
  15017. * chart. One parameter, `event`, is passed to the function, containing
  15018. * common event information.
  15019. *
  15020. * The new user set minimum and maximum values can be found by
  15021. * `event.min` and `event.max`. These reflect the axis minimum and
  15022. * maximum in axis values. The actual data extremes are found in
  15023. * `event.dataMin` and `event.dataMax`.
  15024. *
  15025. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  15026. * @since 2.3
  15027. * @context Highcharts.Axis
  15028. * @apioption xAxis.events.afterSetExtremes
  15029. */
  15030. /**
  15031. * An event fired when a break from this axis occurs on a point.
  15032. *
  15033. * @see [breaks](#xAxis.breaks)
  15034. *
  15035. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  15036. * Visualization of a Break
  15037. *
  15038. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  15039. * @since 4.1.0
  15040. * @product highcharts gantt
  15041. * @context Highcharts.Axis
  15042. * @apioption xAxis.events.pointBreak
  15043. */
  15044. /**
  15045. * An event fired when a point falls inside a break from this axis.
  15046. *
  15047. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  15048. * @product highcharts highstock gantt
  15049. * @context Highcharts.Axis
  15050. * @apioption xAxis.events.pointInBreak
  15051. */
  15052. /**
  15053. * Fires when the minimum and maximum is set for the axis, either by
  15054. * calling the `.setExtremes()` method or by selecting an area in the
  15055. * chart. One parameter, `event`, is passed to the function,
  15056. * containing common event information.
  15057. *
  15058. * The new user set minimum and maximum values can be found by
  15059. * `event.min` and `event.max`. These reflect the axis minimum and
  15060. * maximum in data values. When an axis is zoomed all the way out from
  15061. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  15062. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  15063. *
  15064. * @sample {highstock} stock/xaxis/events-setextremes/
  15065. * Log new extremes on x axis
  15066. *
  15067. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  15068. * @since 1.2.0
  15069. * @context Highcharts.Axis
  15070. * @apioption xAxis.events.setExtremes
  15071. */
  15072. /**
  15073. * The lowest allowed value for automatically computed axis extremes.
  15074. *
  15075. * @see [ceiling](#yAxis.ceiling)
  15076. *
  15077. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  15078. * Floor and ceiling
  15079. * @sample {highstock} stock/demo/lazy-loading/
  15080. * Prevent negative stock price on Y axis
  15081. *
  15082. * @type {number}
  15083. * @since 4.0
  15084. * @product highcharts highstock gantt
  15085. * @apioption xAxis.floor
  15086. */
  15087. /**
  15088. * The dash or dot style of the grid lines. For possible values, see
  15089. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  15090. *
  15091. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  15092. * Long dashes
  15093. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  15094. * Long dashes
  15095. *
  15096. * @type {Highcharts.DashStyleValue}
  15097. * @since 1.2
  15098. */
  15099. gridLineDashStyle: 'Solid',
  15100. /**
  15101. * The Z index of the grid lines.
  15102. *
  15103. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  15104. * A Z index of 4 renders the grid above the graph
  15105. *
  15106. * @product highcharts highstock gantt
  15107. */
  15108. gridZIndex: 1,
  15109. /**
  15110. * An id for the axis. This can be used after render time to get
  15111. * a pointer to the axis object through `chart.get()`.
  15112. *
  15113. * @sample {highcharts} highcharts/xaxis/id/
  15114. * Get the object
  15115. * @sample {highstock} stock/xaxis/id/
  15116. * Get the object
  15117. *
  15118. * @type {string}
  15119. * @since 1.2.0
  15120. * @apioption xAxis.id
  15121. */
  15122. /**
  15123. * The axis labels show the number or category for each tick.
  15124. *
  15125. * Since v8.0.0: Labels are animated in categorized x-axis with
  15126. * updating data if `tickInterval` and `step` is set to 1.
  15127. *
  15128. * @productdesc {highmaps}
  15129. * X and Y axis labels are by default disabled in Highmaps, but the
  15130. * functionality is inherited from Highcharts and used on `colorAxis`,
  15131. * and can be enabled on X and Y axes too.
  15132. */
  15133. labels: {
  15134. /**
  15135. * What part of the string the given position is anchored to.
  15136. * If `left`, the left side of the string is at the axis position.
  15137. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  15138. * an intelligent guess based on which side of the chart the axis
  15139. * is on and the rotation of the label.
  15140. *
  15141. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  15142. *
  15143. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  15144. * Left
  15145. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  15146. * Right
  15147. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  15148. * Left-aligned labels on a vertical category axis
  15149. *
  15150. * @type {Highcharts.AlignValue}
  15151. * @apioption xAxis.labels.align
  15152. */
  15153. /**
  15154. * Whether to allow the axis labels to overlap. When false,
  15155. * overlapping labels are hidden.
  15156. *
  15157. * @sample {highcharts} highcharts/xaxis/labels-allowoverlap-true/
  15158. * X axis labels overlap enabled
  15159. *
  15160. * @type {boolean}
  15161. * @default false
  15162. * @apioption xAxis.labels.allowOverlap
  15163. */
  15164. /**
  15165. * For horizontal axes, the allowed degrees of label rotation
  15166. * to prevent overlapping labels. If there is enough space,
  15167. * labels are not rotated. As the chart gets narrower, it
  15168. * will start rotating the labels -45 degrees, then remove
  15169. * every second label and try again with rotations 0 and -45 etc.
  15170. * Set it to `undefined` to disable rotation, which will
  15171. * cause the labels to word-wrap if possible. Defaults to `[-45]``
  15172. * on bottom and top axes, `undefined` on left and right axes.
  15173. *
  15174. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  15175. * Default auto rotation of 0 or -45
  15176. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  15177. * Custom graded auto rotation
  15178. *
  15179. * @type {Array<number>}
  15180. * @default undefined
  15181. * @since 4.1.0
  15182. * @product highcharts highstock gantt
  15183. * @apioption xAxis.labels.autoRotation
  15184. */
  15185. autoRotation: void 0,
  15186. /**
  15187. * When each category width is more than this many pixels, we don't
  15188. * apply auto rotation. Instead, we lay out the axis label with word
  15189. * wrap. A lower limit makes sense when the label contains multiple
  15190. * short words that don't extend the available horizontal space for
  15191. * each label.
  15192. *
  15193. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  15194. * Lower limit
  15195. *
  15196. * @since 4.1.5
  15197. * @product highcharts gantt
  15198. */
  15199. autoRotationLimit: 80,
  15200. /**
  15201. * The label's pixel distance from the perimeter of the plot area.
  15202. * On cartesian charts, this is overridden if the `labels.y` setting
  15203. * is set.
  15204. *
  15205. * @sample {highcharts} highcharts/yaxis/labels-distance/
  15206. * Polar chart, labels centered under the arc
  15207. *
  15208. * @type {number}
  15209. * @product highcharts gantt
  15210. */
  15211. distance: 15,
  15212. /**
  15213. * Enable or disable the axis labels.
  15214. *
  15215. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  15216. * X axis labels disabled
  15217. * @sample {highstock} stock/xaxis/labels-enabled/
  15218. * X axis labels disabled
  15219. *
  15220. */
  15221. enabled: true,
  15222. /**
  15223. * A format string for the axis label. The context is available as
  15224. * format string variables. For example, you can use `{text}` to
  15225. * insert the default formatted text. The recommended way of adding
  15226. * units for the label is using `text`, for example `{text} km`.
  15227. *
  15228. * To add custom numeric or datetime formatting, use `{value}` with
  15229. * formatting, for example `{value:.1f}` or `{value:%Y-%m-%d}`.
  15230. *
  15231. * See
  15232. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  15233. * for more examples of formatting.
  15234. *
  15235. * The default value is not specified due to the dynamic
  15236. * nature of the default implementation.
  15237. *
  15238. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  15239. * Add units to Y axis label
  15240. * @sample {highcharts} highcharts/xaxis/labels-format-linked/
  15241. * Linked category names
  15242. * @sample {highcharts} highcharts/xaxis/labels-format-custom/
  15243. * Custom number format
  15244. *
  15245. * @type {string}
  15246. * @since 3.0
  15247. * @apioption xAxis.labels.format
  15248. */
  15249. /**
  15250. * Callback JavaScript function to format the label. The value
  15251. * is given by `this.value`. Additional properties for `this` are
  15252. * `axis`, `chart`, `isFirst`, `isLast` and `text` which holds the
  15253. * value of the default formatter.
  15254. *
  15255. * Defaults to a built in function returning a formatted string
  15256. * depending on whether the axis is `category`, `datetime`,
  15257. * `numeric` or other.
  15258. *
  15259. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  15260. * Linked category names
  15261. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  15262. * Modified numeric labels
  15263. * @sample {highstock} stock/xaxis/labels-formatter/
  15264. * Added units on Y axis
  15265. *
  15266. * @type {Highcharts.AxisLabelsFormatterCallbackFunction}
  15267. * @apioption xAxis.labels.formatter
  15268. */
  15269. /**
  15270. * The number of pixels to indent the labels per level in a treegrid
  15271. * axis.
  15272. *
  15273. * @sample gantt/treegrid-axis/demo
  15274. * Indentation 10px by default.
  15275. * @sample gantt/treegrid-axis/indentation-0px
  15276. * Indentation set to 0px.
  15277. *
  15278. * @product gantt
  15279. */
  15280. indentation: 10,
  15281. /**
  15282. * Horizontal axis only. When `staggerLines` is not set,
  15283. * `maxStaggerLines` defines how many lines the axis is allowed to
  15284. * add to automatically avoid overlapping X labels. Set to `1` to
  15285. * disable overlap detection.
  15286. *
  15287. * @deprecated
  15288. * @type {number}
  15289. * @default 5
  15290. * @since 1.3.3
  15291. * @apioption xAxis.labels.maxStaggerLines
  15292. */
  15293. /**
  15294. * How to handle overflowing labels on horizontal axis. If set to
  15295. * `"allow"`, it will not be aligned at all. By default it
  15296. * `"justify"` labels inside the chart area. If there is room to
  15297. * move it, it will be aligned to the edge, else it will be removed.
  15298. *
  15299. * @since 2.2.5
  15300. * @validvalue ["allow", "justify"]
  15301. */
  15302. overflow: 'justify',
  15303. /**
  15304. * The pixel padding for axis labels, to ensure white space between
  15305. * them.
  15306. *
  15307. * @product highcharts gantt
  15308. */
  15309. padding: 5,
  15310. /**
  15311. * Whether to reserve space for the labels. By default, space is
  15312. * reserved for the labels in these cases:
  15313. *
  15314. * * On all horizontal axes.
  15315. * * On vertical axes if `label.align` is `right` on a left-side
  15316. * axis or `left` on a right-side axis.
  15317. * * On vertical axes if `label.align` is `center`.
  15318. *
  15319. * This can be turned off when for example the labels are rendered
  15320. * inside the plot area instead of outside.
  15321. *
  15322. * @see [labels.align](#xAxis.labels.align)
  15323. *
  15324. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  15325. * No reserved space, labels inside plot
  15326. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  15327. * Left-aligned labels on a vertical category axis
  15328. *
  15329. * @type {boolean}
  15330. * @since 4.1.10
  15331. * @product highcharts highstock gantt
  15332. * @apioption xAxis.labels.reserveSpace
  15333. */
  15334. reserveSpace: void 0,
  15335. /**
  15336. * Rotation of the labels in degrees. When `undefined`, the
  15337. * `autoRotation` option takes precedence.
  15338. *
  15339. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  15340. * X axis labels rotated 90°
  15341. *
  15342. * @type {number}
  15343. * @default 0
  15344. * @apioption xAxis.labels.rotation
  15345. */
  15346. rotation: void 0,
  15347. /**
  15348. * Horizontal axes only. The number of lines to spread the labels
  15349. * over to make room or tighter labels. 0 disables staggering.
  15350. *
  15351. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  15352. * Show labels over two lines
  15353. * @sample {highstock} stock/xaxis/labels-staggerlines/
  15354. * Show labels over two lines
  15355. *
  15356. * @since 2.1
  15357. */
  15358. staggerLines: 0,
  15359. /**
  15360. * To show only every _n_'th label on the axis, set the step to _n_.
  15361. * Setting the step to 2 shows every other label.
  15362. *
  15363. * By default, when 0, the step is calculated automatically to avoid
  15364. * overlap. To prevent this, set it to 1\. This usually only
  15365. * happens on a category axis, and is often a sign that you have
  15366. * chosen the wrong axis type.
  15367. *
  15368. * Read more at
  15369. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  15370. * => What axis should I use?
  15371. *
  15372. * @sample {highcharts} highcharts/xaxis/labels-step/
  15373. * Showing only every other axis label on a categorized
  15374. * x-axis
  15375. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  15376. * Auto steps on a category axis
  15377. *
  15378. * @since 2.1
  15379. */
  15380. step: 0,
  15381. /**
  15382. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  15383. * to render the labels.
  15384. */
  15385. useHTML: false,
  15386. /**
  15387. * The x position offset of all labels relative to the tick
  15388. * positions on the axis. Overrides the `labels.distance` option.
  15389. *
  15390. * @type {number}
  15391. * @apioption xAxis.labels.x
  15392. */
  15393. /**
  15394. * The y position offset of all labels relative to the tick
  15395. * positions on the axis. Overrides the `labels.distance` option.
  15396. *
  15397. * @sample {highcharts} highcharts/xaxis/labels-x/
  15398. * X axis labels placed on grid lines
  15399. *
  15400. * @type {number}
  15401. * @apioption xAxis.labels.y
  15402. */
  15403. /**
  15404. * The Z index for the axis labels.
  15405. */
  15406. zIndex: 7,
  15407. /**
  15408. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  15409. * wrapping of category labels. Use `textOverflow: 'none'` to
  15410. * prevent ellipsis (dots).
  15411. *
  15412. * In styled mode, the labels are styled with the
  15413. * `.highcharts-axis-labels` class.
  15414. *
  15415. * @sample {highcharts} highcharts/xaxis/labels-style/
  15416. * Red X axis labels
  15417. *
  15418. * @type {Highcharts.CSSObject}
  15419. */
  15420. style: {
  15421. /** @internal */
  15422. color: "#333333" /* Palette.neutralColor80 */,
  15423. /** @internal */
  15424. cursor: 'default',
  15425. /** @internal */
  15426. fontSize: '0.8em'
  15427. }
  15428. },
  15429. /**
  15430. * The left position as the horizontal axis. If it's a number, it is
  15431. * interpreted as pixel position relative to the chart.
  15432. *
  15433. * Since Highcharts v5.0.13: If it's a percentage string, it is
  15434. * interpreted as percentages of the plot width, offset from plot area
  15435. * left.
  15436. *
  15437. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  15438. * Different axis position properties
  15439. *
  15440. * @type {number|string}
  15441. * @product highcharts highstock
  15442. * @apioption xAxis.left
  15443. */
  15444. /**
  15445. * The top position as the vertical axis. If it's a number, it is
  15446. * interpreted as pixel position relative to the chart.
  15447. *
  15448. * Since Highcharts 2: If it's a percentage string, it is interpreted
  15449. * as percentages of the plot height, offset from plot area top.
  15450. *
  15451. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  15452. * Different axis position properties
  15453. *
  15454. * @type {number|string}
  15455. * @product highcharts highstock
  15456. * @apioption xAxis.top
  15457. */
  15458. /**
  15459. * Index of another axis that this axis is linked to. When an axis is
  15460. * linked to a master axis, it will take the same extremes as
  15461. * the master, but as assigned by min or max or by setExtremes.
  15462. * It can be used to show additional info, or to ease reading the
  15463. * chart by duplicating the scales.
  15464. *
  15465. * @sample {highcharts} highcharts/xaxis/linkedto/
  15466. * Different string formats of the same date
  15467. * @sample {highcharts} highcharts/yaxis/linkedto/
  15468. * Y values on both sides
  15469. *
  15470. * @type {number}
  15471. * @since 2.0.2
  15472. * @product highcharts highstock gantt
  15473. * @apioption xAxis.linkedTo
  15474. */
  15475. /**
  15476. * The maximum value of the axis. If `null`, the max value is
  15477. * automatically calculated.
  15478. *
  15479. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  15480. * might be rounded up.
  15481. *
  15482. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  15483. * beyond the set max in order to reach the given number of ticks. The
  15484. * same may happen in a chart with multiple axes, determined by [chart.
  15485. * alignTicks](#chart), where a `tickAmount` is applied internally.
  15486. *
  15487. * @sample {highcharts} highcharts/yaxis/max-200/
  15488. * Y axis max of 200
  15489. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  15490. * Y axis max on logarithmic axis
  15491. * @sample {highstock} stock/xaxis/min-max/
  15492. * Fixed min and max on X axis
  15493. *
  15494. * @type {number|null}
  15495. * @apioption xAxis.max
  15496. */
  15497. /**
  15498. * Padding of the max value relative to the length of the axis. A
  15499. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15500. * when you don't want the highest data value to appear on the edge
  15501. * of the plot area. When the axis' `max` option is set or a max extreme
  15502. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15503. *
  15504. * @productdesc {highstock}
  15505. * For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
  15506. * are ignored. Use [overscroll](#xAxis.overscroll) instead.
  15507. *
  15508. * @sample {highcharts} highcharts/yaxis/maxpadding/
  15509. * Max padding of 0.25 on y axis
  15510. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15511. * Greater min- and maxPadding
  15512. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  15513. * Add some padding
  15514. *
  15515. * @default {highcharts} 0.01
  15516. * @default {highstock|highmaps} 0
  15517. * @since 1.2.0
  15518. */
  15519. maxPadding: 0.01,
  15520. /**
  15521. * Deprecated. Use `minRange` instead.
  15522. *
  15523. * @deprecated
  15524. * @type {number}
  15525. * @product highcharts highstock
  15526. * @apioption xAxis.maxZoom
  15527. */
  15528. /**
  15529. * The minimum value of the axis. If `null` the min value is
  15530. * automatically calculated.
  15531. *
  15532. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  15533. * the `min` value might be rounded down.
  15534. *
  15535. * The automatically calculated minimum value is also affected by
  15536. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  15537. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  15538. * as well as [series.threshold](#plotOptions.series.threshold)
  15539. * and [series.softThreshold](#plotOptions.series.softThreshold).
  15540. *
  15541. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  15542. * -50 with startOnTick to false
  15543. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  15544. * -50 with startOnTick true by default
  15545. * @sample {highstock} stock/xaxis/min-max/
  15546. * Set min and max on X axis
  15547. *
  15548. * @type {number|null}
  15549. * @apioption xAxis.min
  15550. */
  15551. /**
  15552. * The dash or dot style of the minor grid lines. For possible values,
  15553. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  15554. *
  15555. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  15556. * Long dashes on minor grid lines
  15557. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  15558. * Long dashes on minor grid lines
  15559. *
  15560. * @type {Highcharts.DashStyleValue}
  15561. * @since 1.2
  15562. */
  15563. minorGridLineDashStyle: 'Solid',
  15564. /**
  15565. * Specific tick interval in axis units for the minor ticks. On a linear
  15566. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  15567. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  15568. * shown.
  15569. *
  15570. * On logarithmic axes, the unit is the power of the value. For example,
  15571. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  15572. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  15573. * between 1 and 10, 10 and 100 etc.
  15574. *
  15575. * If user settings dictate minor ticks to become too dense, they don't
  15576. * make sense, and will be ignored to prevent performance problems.
  15577. *
  15578. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  15579. * Null by default
  15580. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  15581. * 5 units
  15582. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  15583. * "auto"
  15584. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  15585. * 0.1
  15586. * @sample {highstock} stock/demo/basic-line/
  15587. * Null by default
  15588. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  15589. * "auto"
  15590. *
  15591. * @type {number|string|null}
  15592. * @apioption xAxis.minorTickInterval
  15593. */
  15594. /**
  15595. * The pixel length of the minor tick marks.
  15596. *
  15597. * @sample {highcharts} highcharts/yaxis/minorticklength/
  15598. * 10px on Y axis
  15599. * @sample {highstock} stock/xaxis/minorticks/
  15600. * 10px on Y axis
  15601. */
  15602. minorTickLength: 2,
  15603. /**
  15604. * The position of the minor tick marks relative to the axis line.
  15605. * Can be one of `inside` and `outside`.
  15606. *
  15607. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  15608. * Outside by default
  15609. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  15610. * Inside
  15611. * @sample {highstock} stock/xaxis/minorticks/
  15612. * Inside
  15613. *
  15614. * @validvalue ["inside", "outside"]
  15615. */
  15616. minorTickPosition: 'outside',
  15617. /**
  15618. * Enable or disable minor ticks. The interval between the minor ticks
  15619. * can be controlled either by the
  15620. * [minorTicksPerMajor](#xAxis.minorTicksPerMajor) setting, or as an
  15621. * absolute [minorTickInterval](#xAxis.minorTickInterval) value.
  15622. *
  15623. * On a logarithmic axis, minor ticks are laid out based on a best
  15624. * guess, attempting to enter an approximate number of minor ticks
  15625. * between each major tick based on
  15626. * [minorTicksPerMajor](#xAxis.minorTicksPerMajor).
  15627. *
  15628. * Prior to v6.0.0, ticks were enabled in auto layout by setting
  15629. * `minorTickInterval` to `"auto"`.
  15630. *
  15631. * @productdesc {highcharts} On axes using
  15632. * [categories](#xAxis.categories), minor ticks are not supported.
  15633. *
  15634. * @sample {highcharts} highcharts/yaxis/minorticks-true/ Enabled on
  15635. * linear Y axis
  15636. *
  15637. * @type {boolean}
  15638. * @default false
  15639. * @since 6.0.0
  15640. * @apioption xAxis.minorTicks
  15641. */
  15642. /**
  15643. * The number of minor ticks per major tick. Works for `linear`,
  15644. * `logarithmic` and `datetime` axes.
  15645. *
  15646. * @sample {highcharts} highcharts/yaxis/minortickspermajor/
  15647. * 2 minor ticks per major tick on Y axis
  15648. *
  15649. * @type {number}
  15650. */
  15651. minorTicksPerMajor: 5,
  15652. /**
  15653. * The pixel width of the minor tick mark.
  15654. *
  15655. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  15656. * 3px width
  15657. * @sample {highstock} stock/xaxis/minorticks/
  15658. * 1px width
  15659. *
  15660. * @type {number}
  15661. * @default 0
  15662. * @apioption xAxis.minorTickWidth
  15663. */
  15664. /**
  15665. * Padding of the min value relative to the length of the axis. A
  15666. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15667. * when you don't want the lowest data value to appear on the edge
  15668. * of the plot area. When the axis' `min` option is set or a min extreme
  15669. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  15670. *
  15671. * @productdesc {highstock}
  15672. * For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
  15673. * are ignored. Use [overscroll](#xAxis.overscroll) instead.
  15674. *
  15675. * @sample {highcharts} highcharts/yaxis/minpadding/
  15676. * Min padding of 0.2
  15677. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15678. * Greater min- and maxPadding
  15679. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  15680. * Add some padding
  15681. *
  15682. * @default {highcharts} 0.01
  15683. * @default {highstock|highmaps} 0
  15684. * @since 1.2.0
  15685. * @product highcharts highstock gantt
  15686. */
  15687. minPadding: 0.01,
  15688. /**
  15689. * The minimum range to display on this axis. The entire axis will not
  15690. * be allowed to span over a smaller interval than this. For example,
  15691. * for a datetime axis the main unit is milliseconds. If minRange is
  15692. * set to 3600000, you can't zoom in more than to one hour.
  15693. *
  15694. * The default minRange for the x axis is five times the smallest
  15695. * interval between any of the data points.
  15696. *
  15697. * On a logarithmic axis, the unit for the minimum range is the power.
  15698. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  15699. * 100-1000, 1000-10000 etc.
  15700. *
  15701. * **Note**: The `minPadding`, `maxPadding`, `startOnTick` and
  15702. * `endOnTick` settings also affect how the extremes of the axis
  15703. * are computed.
  15704. *
  15705. * @sample {highcharts} highcharts/xaxis/minrange/
  15706. * Minimum range of 5
  15707. * @sample {highstock} stock/xaxis/minrange/
  15708. * Max zoom of 6 months overrides user selections
  15709. *
  15710. * @type {number}
  15711. * @apioption xAxis.minRange
  15712. */
  15713. /**
  15714. * The minimum tick interval allowed in axis values. For example on
  15715. * zooming in on an axis with daily data, this can be used to prevent
  15716. * the axis from showing hours. Defaults to the closest distance between
  15717. * two points on the axis.
  15718. *
  15719. * @type {number}
  15720. * @since 2.3.0
  15721. * @apioption xAxis.minTickInterval
  15722. */
  15723. /**
  15724. * The distance in pixels from the plot area to the axis line.
  15725. * A positive offset moves the axis with it's line, labels and ticks
  15726. * away from the plot area. This is typically used when two or more
  15727. * axes are displayed on the same side of the plot. With multiple
  15728. * axes the offset is dynamically adjusted to avoid collision, this
  15729. * can be overridden by setting offset explicitly.
  15730. *
  15731. * @sample {highcharts} highcharts/yaxis/offset/
  15732. * Y axis offset of 70
  15733. * @sample {highcharts} highcharts/yaxis/offset-centered/
  15734. * Axes positioned in the center of the plot
  15735. * @sample {highstock} stock/xaxis/offset/
  15736. * Y axis offset by 70 px
  15737. *
  15738. * @type {number}
  15739. */
  15740. offset: void 0,
  15741. /**
  15742. * Whether to display the axis on the opposite side of the normal. The
  15743. * normal is on the left side for vertical axes and bottom for
  15744. * horizontal, so the opposite sides will be right and top respectively.
  15745. * This is typically used with dual or multiple axes.
  15746. *
  15747. * @sample {highcharts} highcharts/yaxis/opposite/
  15748. * Secondary Y axis opposite
  15749. * @sample {highstock} stock/xaxis/opposite/
  15750. * Y axis on left side
  15751. *
  15752. * @default {highcharts|highstock|highmaps} false
  15753. * @default {gantt} true
  15754. */
  15755. opposite: false,
  15756. /**
  15757. * In an ordinal axis, the points are equally spaced in the chart
  15758. * regardless of the actual time or x distance between them. This means
  15759. * that missing data periods (e.g. nights or weekends for a stock chart)
  15760. * will not take up space in the chart.
  15761. * Having `ordinal: false` will show any gaps created by the `gapSize`
  15762. * setting proportionate to their duration.
  15763. *
  15764. * In stock charts the X axis is ordinal by default, unless
  15765. * the boost module is used and at least one of the series' data length
  15766. * exceeds the [boostThreshold](#series.line.boostThreshold).
  15767. *
  15768. * For an ordinal axis, `minPadding` and `maxPadding` are ignored. Use
  15769. * [overscroll](#xAxis.overscroll) instead.
  15770. *
  15771. * @sample {highstock} stock/xaxis/ordinal-true/
  15772. * True by default
  15773. * @sample {highstock} stock/xaxis/ordinal-false/
  15774. * False
  15775. *
  15776. * @see [overscroll](#xAxis.overscroll)
  15777. *
  15778. * @type {boolean}
  15779. * @default true
  15780. * @since 1.1
  15781. * @product highstock
  15782. * @apioption xAxis.ordinal
  15783. */
  15784. /**
  15785. * Additional range on the right side of the xAxis. Works similar to
  15786. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  15787. * both main `xAxis` and the navigator's `xAxis`.
  15788. *
  15789. * @sample {highstock} stock/xaxis/overscroll/
  15790. * One minute overscroll with live data
  15791. *
  15792. * @type {number}
  15793. * @default 0
  15794. * @since 6.0.0
  15795. * @product highstock
  15796. * @apioption xAxis.overscroll
  15797. */
  15798. /**
  15799. * Refers to the index in the [panes](#panes) array. Used for circular
  15800. * gauges and polar charts. When the option is not set then first pane
  15801. * will be used.
  15802. *
  15803. * @sample highcharts/demo/gauge-vu-meter
  15804. * Two gauges with different center
  15805. *
  15806. * @type {number}
  15807. * @product highcharts
  15808. * @apioption xAxis.pane
  15809. */
  15810. /**
  15811. * The zoomed range to display when only defining one or none of `min`
  15812. * or `max`. For example, to show the latest month, a range of one month
  15813. * can be set.
  15814. *
  15815. * @sample {highstock} stock/xaxis/range/
  15816. * Setting a zoomed range when the rangeSelector is disabled
  15817. *
  15818. * @type {number}
  15819. * @product highstock
  15820. * @apioption xAxis.range
  15821. */
  15822. /**
  15823. * Whether to reverse the axis so that the highest number is closest
  15824. * to the origin. If the chart is inverted, the x axis is reversed by
  15825. * default.
  15826. *
  15827. * @sample {highcharts} highcharts/yaxis/reversed/
  15828. * Reversed Y axis
  15829. * @sample {highstock} stock/xaxis/reversed/
  15830. * Reversed Y axis
  15831. *
  15832. * @type {boolean}
  15833. * @default undefined
  15834. * @apioption xAxis.reversed
  15835. */
  15836. reversed: void 0,
  15837. /**
  15838. * This option determines how stacks should be ordered within a group.
  15839. * For example reversed xAxis also reverses stacks, so first series
  15840. * comes last in a group. To keep order like for non-reversed xAxis
  15841. * enable this option.
  15842. *
  15843. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  15844. * Reversed stacks comparison
  15845. * @sample {highstock} highcharts/xaxis/reversedstacks/
  15846. * Reversed stacks comparison
  15847. *
  15848. * @since 6.1.1
  15849. * @product highcharts highstock
  15850. */
  15851. reversedStacks: false,
  15852. /**
  15853. * An optional scrollbar to display on the X axis in response to
  15854. * limiting the minimum and maximum of the axis values.
  15855. *
  15856. * In styled mode, all the presentational options for the scrollbar are
  15857. * replaced by the classes `.highcharts-scrollbar-thumb`,
  15858. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  15859. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  15860. *
  15861. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  15862. * Heatmap with both scrollbars
  15863. *
  15864. * @extends scrollbar
  15865. * @since 4.2.6
  15866. * @product highstock
  15867. * @apioption xAxis.scrollbar
  15868. */
  15869. /**
  15870. * Whether to show the axis line and title when the axis has no data.
  15871. *
  15872. * @sample {highcharts} highcharts/yaxis/showempty/
  15873. * When clicking the legend to hide series, one axis preserves
  15874. * line and title, the other doesn't
  15875. * @sample {highstock} highcharts/yaxis/showempty/
  15876. * When clicking the legend to hide series, one axis preserves
  15877. * line and title, the other doesn't
  15878. *
  15879. * @since 1.1
  15880. */
  15881. showEmpty: true,
  15882. /**
  15883. * Whether to show the first tick label.
  15884. *
  15885. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  15886. * Set to false on X axis
  15887. * @sample {highstock} stock/xaxis/showfirstlabel/
  15888. * Labels below plot lines on Y axis
  15889. */
  15890. showFirstLabel: true,
  15891. /**
  15892. * Whether to show the last tick label. Defaults to `true` on cartesian
  15893. * charts, and `false` on polar charts.
  15894. *
  15895. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  15896. * Set to true on X axis
  15897. * @sample {highstock} stock/xaxis/showfirstlabel/
  15898. * Labels below plot lines on Y axis
  15899. *
  15900. * @type {boolean}
  15901. * @default undefined
  15902. * @product highcharts highstock gantt
  15903. */
  15904. showLastLabel: true,
  15905. /**
  15906. * A soft maximum for the axis. If the series data maximum is less than
  15907. * this, the axis will stay at this maximum, but if the series data
  15908. * maximum is higher, the axis will flex to show all data.
  15909. *
  15910. * @sample highcharts/yaxis/softmin-softmax/
  15911. * Soft min and max
  15912. *
  15913. * @type {number}
  15914. * @since 5.0.1
  15915. * @product highcharts highstock gantt
  15916. * @apioption xAxis.softMax
  15917. */
  15918. /**
  15919. * A soft minimum for the axis. If the series data minimum is greater
  15920. * than this, the axis will stay at this minimum, but if the series
  15921. * data minimum is lower, the axis will flex to show all data.
  15922. *
  15923. * @sample highcharts/yaxis/softmin-softmax/
  15924. * Soft min and max
  15925. *
  15926. * @type {number}
  15927. * @since 5.0.1
  15928. * @product highcharts highstock gantt
  15929. * @apioption xAxis.softMin
  15930. */
  15931. /**
  15932. * For datetime axes, this decides where to put the tick between weeks.
  15933. * 0 = Sunday, 1 = Monday.
  15934. *
  15935. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  15936. * Monday by default
  15937. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  15938. * Sunday
  15939. * @sample {highstock} stock/xaxis/startofweek-1
  15940. * Monday by default
  15941. * @sample {highstock} stock/xaxis/startofweek-0
  15942. * Sunday
  15943. *
  15944. * @product highcharts highstock gantt
  15945. */
  15946. startOfWeek: 1,
  15947. /**
  15948. * Whether to force the axis to start on a tick. Use this option with
  15949. * the `minPadding` option to control the axis start.
  15950. *
  15951. * @productdesc {highstock}
  15952. * In Highcharts Stock, `startOnTick` is always `false` when
  15953. * the navigator is enabled, to prevent jumpy scrolling.
  15954. *
  15955. * @sample {highcharts} highcharts/xaxis/startontick-false/
  15956. * False by default
  15957. * @sample {highcharts} highcharts/xaxis/startontick-true/
  15958. * True
  15959. *
  15960. * @since 1.2.0
  15961. */
  15962. startOnTick: false,
  15963. /**
  15964. * The amount of ticks to draw on the axis. This opens up for aligning
  15965. * the ticks of multiple charts or panes within a chart. This option
  15966. * overrides the `tickPixelInterval` option.
  15967. *
  15968. * This option only has an effect on linear axes. Datetime, logarithmic
  15969. * or category axes are not affected.
  15970. *
  15971. * @sample {highcharts} highcharts/yaxis/tickamount/
  15972. * 8 ticks on Y axis
  15973. * @sample {highstock} highcharts/yaxis/tickamount/
  15974. * 8 ticks on Y axis
  15975. *
  15976. * @type {number}
  15977. * @since 4.1.0
  15978. * @product highcharts highstock gantt
  15979. * @apioption xAxis.tickAmount
  15980. */
  15981. /**
  15982. * The interval of the tick marks in axis units. When `undefined`, the
  15983. * tick interval is computed to approximately follow the
  15984. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  15985. * axes. On categorized axes, a `undefined` tickInterval will default to
  15986. * 1, one category. Note that datetime axes are based on milliseconds,
  15987. * so for example an interval of one day is expressed as
  15988. * `24 * 3600 * 1000`.
  15989. *
  15990. * On logarithmic axes, the tickInterval is based on powers, so a
  15991. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  15992. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  15993. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  15994. * 40 etc.
  15995. *
  15996. *
  15997. * If the tickInterval is too dense for labels to be drawn, Highcharts
  15998. * may remove ticks.
  15999. *
  16000. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  16001. * option may interfere with the `tickInterval` setting.
  16002. *
  16003. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  16004. * @see [tickPositions](#xAxis.tickPositions)
  16005. * @see [tickPositioner](#xAxis.tickPositioner)
  16006. *
  16007. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  16008. * Tick interval of 5 on a linear axis
  16009. * @sample {highstock} stock/xaxis/tickinterval/
  16010. * Tick interval of 0.01 on Y axis
  16011. *
  16012. * @type {number}
  16013. * @apioption xAxis.tickInterval
  16014. */
  16015. /**
  16016. * The pixel length of the main tick marks.
  16017. *
  16018. * @sample {highcharts} highcharts/xaxis/ticklength/
  16019. * 20 px tick length on the X axis
  16020. * @sample {highstock} stock/xaxis/ticks/
  16021. * Formatted ticks on X axis
  16022. */
  16023. tickLength: 10,
  16024. /**
  16025. * If tickInterval is `null` this option sets the approximate pixel
  16026. * interval of the tick marks. Not applicable to categorized axis.
  16027. *
  16028. * The tick interval is also influenced by the [minTickInterval](
  16029. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  16030. * being denser than the data points.
  16031. *
  16032. * @see [tickInterval](#xAxis.tickInterval)
  16033. * @see [tickPositioner](#xAxis.tickPositioner)
  16034. * @see [tickPositions](#xAxis.tickPositions)
  16035. *
  16036. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  16037. * 50 px on X axis
  16038. * @sample {highstock} stock/xaxis/tickpixelinterval/
  16039. * 200 px on X axis
  16040. */
  16041. tickPixelInterval: 100,
  16042. /**
  16043. * For categorized axes only. If `on` the tick mark is placed in the
  16044. * center of the category, if `between` the tick mark is placed between
  16045. * categories. The default is `between` if the `tickInterval` is 1, else
  16046. * `on`.
  16047. *
  16048. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  16049. * "between" by default
  16050. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  16051. * "on"
  16052. *
  16053. * @product highcharts gantt
  16054. * @validvalue ["on", "between"]
  16055. */
  16056. tickmarkPlacement: 'between',
  16057. /**
  16058. * The position of the major tick marks relative to the axis line.
  16059. * Can be one of `inside` and `outside`.
  16060. *
  16061. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  16062. * "outside" by default
  16063. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  16064. * "inside"
  16065. * @sample {highstock} stock/xaxis/ticks/
  16066. * Formatted ticks on X axis
  16067. *
  16068. * @validvalue ["inside", "outside"]
  16069. */
  16070. tickPosition: 'outside',
  16071. /**
  16072. * A callback function returning array defining where the ticks are
  16073. * laid out on the axis. This overrides the default behaviour of
  16074. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  16075. * #xAxis.tickInterval). The automatic tick positions are accessible
  16076. * through `this.tickPositions` and can be modified by the callback.
  16077. *
  16078. * @see [tickPositions](#xAxis.tickPositions)
  16079. *
  16080. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  16081. * Demo of tickPositions and tickPositioner
  16082. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  16083. * Demo of tickPositions and tickPositioner
  16084. *
  16085. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  16086. * @apioption xAxis.tickPositioner
  16087. */
  16088. /**
  16089. * An array defining where the ticks are laid out on the axis. This
  16090. * overrides the default behaviour of [tickPixelInterval](
  16091. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  16092. *
  16093. * @see [tickPositioner](#xAxis.tickPositioner)
  16094. *
  16095. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  16096. * Demo of tickPositions and tickPositioner
  16097. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  16098. * Demo of tickPositions and tickPositioner
  16099. *
  16100. * @type {Array<number>}
  16101. * @apioption xAxis.tickPositions
  16102. */
  16103. /**
  16104. * The pixel width of the major tick marks. Defaults to 0 on category
  16105. * axes, otherwise 1.
  16106. *
  16107. * In styled mode, the stroke width is given in the `.highcharts-tick`
  16108. * class, but in order for the element to be generated on category axes,
  16109. * the option must be explicitly set to 1.
  16110. *
  16111. * @sample {highcharts} highcharts/xaxis/tickwidth/
  16112. * 10 px width
  16113. * @sample {highcharts} highcharts/css/axis-grid/
  16114. * Styled mode
  16115. * @sample {highstock} stock/xaxis/ticks/
  16116. * Formatted ticks on X axis
  16117. * @sample {highstock} highcharts/css/axis-grid/
  16118. * Styled mode
  16119. *
  16120. * @type {undefined|number}
  16121. * @default {highstock} 1
  16122. * @default {highmaps} 0
  16123. * @apioption xAxis.tickWidth
  16124. */
  16125. /**
  16126. * The axis title, showing next to the axis line.
  16127. *
  16128. * @productdesc {highmaps}
  16129. * In Highmaps, the axis is hidden by default, but adding an axis title
  16130. * is still possible. X axis and Y axis titles will appear at the bottom
  16131. * and left by default.
  16132. */
  16133. title: {
  16134. /**
  16135. * Alignment of the title relative to the axis values. Possible
  16136. * values are "low", "middle" or "high".
  16137. *
  16138. * @sample {highcharts} highcharts/xaxis/title-align-low/
  16139. * "low"
  16140. * @sample {highcharts} highcharts/xaxis/title-align-center/
  16141. * "middle" by default
  16142. * @sample {highcharts} highcharts/xaxis/title-align-high/
  16143. * "high"
  16144. * @sample {highcharts} highcharts/yaxis/title-offset/
  16145. * Place the Y axis title on top of the axis
  16146. * @sample {highstock} stock/xaxis/title-align/
  16147. * Aligned to "high" value
  16148. *
  16149. * @type {Highcharts.AxisTitleAlignValue}
  16150. */
  16151. align: 'middle',
  16152. /**
  16153. * Deprecated. Set the `text` to `undefined` to disable the title.
  16154. *
  16155. * @deprecated
  16156. * @type {boolean}
  16157. * @product highcharts
  16158. * @apioption xAxis.title.enabled
  16159. */
  16160. /**
  16161. * The pixel distance between the axis labels or line and the title.
  16162. * Defaults to 0 for horizontal axes, 10 for vertical
  16163. *
  16164. * @sample {highcharts} highcharts/xaxis/title-margin/
  16165. * Y axis title margin of 60
  16166. *
  16167. * @type {number}
  16168. * @apioption xAxis.title.margin
  16169. */
  16170. /**
  16171. * The distance of the axis title from the axis line. By default,
  16172. * this distance is computed from the offset width of the labels,
  16173. * the labels' distance from the axis and the title's margin.
  16174. * However when the offset option is set, it overrides all this.
  16175. *
  16176. * @sample {highcharts} highcharts/yaxis/title-offset/
  16177. * Place the axis title on top of the axis
  16178. * @sample {highstock} highcharts/yaxis/title-offset/
  16179. * Place the axis title on top of the Y axis
  16180. *
  16181. * @type {number}
  16182. * @since 2.2.0
  16183. * @apioption xAxis.title.offset
  16184. */
  16185. /**
  16186. * Whether to reserve space for the title when laying out the axis.
  16187. *
  16188. * @type {boolean}
  16189. * @default true
  16190. * @since 5.0.11
  16191. * @product highcharts highstock gantt
  16192. * @apioption xAxis.title.reserveSpace
  16193. */
  16194. /**
  16195. * The rotation of the text in degrees. 0 is horizontal, 270 is
  16196. * vertical reading from bottom to top.
  16197. *
  16198. * @sample {highcharts} highcharts/yaxis/title-offset/
  16199. * Horizontal
  16200. */
  16201. rotation: 0,
  16202. /**
  16203. * The actual text of the axis title. It can contain basic HTML tags
  16204. * like `b`, `i` and `span` with style.
  16205. *
  16206. * @sample {highcharts} highcharts/xaxis/title-text/
  16207. * Custom HTML
  16208. * @sample {highstock} stock/xaxis/title-text/
  16209. * Titles for both axes
  16210. *
  16211. * @type {string|null}
  16212. * @apioption xAxis.title.text
  16213. */
  16214. /**
  16215. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  16216. * Default alignment depends on the
  16217. * [title.align](xAxis.title.align):
  16218. *
  16219. * Horizontal axes:
  16220. * - for `align` = `"low"`, `textAlign` is set to `left`
  16221. * - for `align` = `"middle"`, `textAlign` is set to `center`
  16222. * - for `align` = `"high"`, `textAlign` is set to `right`
  16223. *
  16224. * Vertical axes:
  16225. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  16226. * set to `right`
  16227. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  16228. * set to `left`
  16229. * - for `align` = `"middle"`, `textAlign` is set to `center`
  16230. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  16231. * set to `left`
  16232. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  16233. * set to `right`
  16234. *
  16235. * @type {Highcharts.AlignValue}
  16236. * @apioption xAxis.title.textAlign
  16237. */
  16238. /**
  16239. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  16240. * to render the axis title.
  16241. *
  16242. * @product highcharts highstock gantt
  16243. */
  16244. useHTML: false,
  16245. /**
  16246. * Horizontal pixel offset of the title position.
  16247. *
  16248. * @since 4.1.6
  16249. * @product highcharts highstock gantt
  16250. */
  16251. x: 0,
  16252. /**
  16253. * Vertical pixel offset of the title position.
  16254. *
  16255. * @product highcharts highstock gantt
  16256. */
  16257. y: 0,
  16258. /**
  16259. * CSS styles for the title. If the title text is longer than the
  16260. * axis length, it will wrap to multiple lines by default. This can
  16261. * be customized by setting `textOverflow: 'ellipsis'`, by
  16262. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  16263. *
  16264. * In styled mode, the stroke width is given in the
  16265. * `.highcharts-axis-title` class.
  16266. *
  16267. * @sample {highcharts} highcharts/xaxis/title-style/
  16268. * Red
  16269. * @sample {highcharts} highcharts/css/axis/
  16270. * Styled mode
  16271. *
  16272. * @type {Highcharts.CSSObject}
  16273. */
  16274. style: {
  16275. /** @internal */
  16276. color: "#666666" /* Palette.neutralColor60 */,
  16277. /** @internal */
  16278. fontSize: '0.8em'
  16279. }
  16280. },
  16281. /**
  16282. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  16283. * or `category`. In a datetime axis, the numbers are given in
  16284. * milliseconds, and tick marks are placed on appropriate values like
  16285. * full hours or days. In a category axis, the
  16286. * [point names](#series.line.data.name) of the chart's series are used
  16287. * for categories, if not a [categories](#xAxis.categories) array is
  16288. * defined.
  16289. *
  16290. * @sample {highcharts} highcharts/xaxis/type-linear/
  16291. * Linear
  16292. * @sample {highcharts} highcharts/yaxis/type-log/
  16293. * Logarithmic
  16294. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  16295. * Logarithmic with minor grid lines
  16296. * @sample {highcharts} highcharts/xaxis/type-log-both/
  16297. * Logarithmic on two axes
  16298. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  16299. * Logarithmic with extension to emulate negative values
  16300. *
  16301. * @type {Highcharts.AxisTypeValue}
  16302. * @product highcharts gantt
  16303. */
  16304. type: 'linear',
  16305. /**
  16306. * If there are multiple axes on the same side of the chart, the pixel
  16307. * margin between the axes. Defaults to 0 on vertical axes, 15 on
  16308. * horizontal axes.
  16309. *
  16310. * @type {number}
  16311. * @since 7.0.3
  16312. * @apioption xAxis.margin
  16313. */
  16314. /**
  16315. * Applies only when the axis `type` is `category`. When `uniqueNames`
  16316. * is true, points are placed on the X axis according to their names.
  16317. * If the same point name is repeated in the same or another series,
  16318. * the point is placed on the same X position as other points of the
  16319. * same name. When `uniqueNames` is false, the points are laid out in
  16320. * increasing X positions regardless of their names, and the X axis
  16321. * category will take the name of the last point in each position.
  16322. *
  16323. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  16324. * True by default
  16325. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  16326. * False
  16327. *
  16328. * @since 4.2.7
  16329. * @product highcharts gantt
  16330. */
  16331. uniqueNames: true,
  16332. /**
  16333. * Datetime axis only. An array determining what time intervals the
  16334. * ticks are allowed to fall on. Each array item is an array where the
  16335. * first value is the time unit and the second value another array of
  16336. * allowed multiples.
  16337. *
  16338. * Defaults to:
  16339. * ```js
  16340. * units: [[
  16341. * 'millisecond', // unit name
  16342. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  16343. * ], [
  16344. * 'second',
  16345. * [1, 2, 5, 10, 15, 30]
  16346. * ], [
  16347. * 'minute',
  16348. * [1, 2, 5, 10, 15, 30]
  16349. * ], [
  16350. * 'hour',
  16351. * [1, 2, 3, 4, 6, 8, 12]
  16352. * ], [
  16353. * 'day',
  16354. * [1, 2]
  16355. * ], [
  16356. * 'week',
  16357. * [1, 2]
  16358. * ], [
  16359. * 'month',
  16360. * [1, 2, 3, 4, 6]
  16361. * ], [
  16362. * 'year',
  16363. * null
  16364. * ]]
  16365. * ```
  16366. *
  16367. * @sample {highcharts} highcharts/xaxis/units/
  16368. * Axis units demonstrated
  16369. *
  16370. * @type {Array<Array<string,(Array<number>|null)>>}
  16371. * @product highcharts highstock gantt
  16372. * @apioption xAxis.units
  16373. */
  16374. /**
  16375. * Whether axis, including axis title, line, ticks and labels, should
  16376. * be visible.
  16377. *
  16378. * @since 4.1.9
  16379. * @product highcharts highstock gantt
  16380. */
  16381. visible: true,
  16382. /**
  16383. * Color of the minor, secondary grid lines.
  16384. *
  16385. * In styled mode, the stroke width is given in the
  16386. * `.highcharts-minor-grid-line` class.
  16387. *
  16388. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  16389. * Bright grey lines from Y axis
  16390. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16391. * Styled mode
  16392. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  16393. * Bright grey lines from Y axis
  16394. *
  16395. * @type {Highcharts.ColorType}
  16396. * @default #f2f2f2
  16397. */
  16398. minorGridLineColor: "#f2f2f2" /* Palette.neutralColor5 */,
  16399. /**
  16400. * Width of the minor, secondary grid lines.
  16401. *
  16402. * In styled mode, the stroke width is given in the
  16403. * `.highcharts-grid-line` class.
  16404. *
  16405. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  16406. * 2px lines from Y axis
  16407. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16408. * Styled mode
  16409. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  16410. * 2px lines from Y axis
  16411. */
  16412. minorGridLineWidth: 1,
  16413. /**
  16414. * Color for the minor tick marks.
  16415. *
  16416. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  16417. * Black tick marks on Y axis
  16418. * @sample {highstock} stock/xaxis/minorticks/
  16419. * Black tick marks on Y axis
  16420. *
  16421. * @type {Highcharts.ColorType}
  16422. * @default #999999
  16423. */
  16424. minorTickColor: "#999999" /* Palette.neutralColor40 */,
  16425. /**
  16426. * The color of the line marking the axis itself.
  16427. *
  16428. * In styled mode, the line stroke is given in the
  16429. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  16430. *
  16431. * @sample {highcharts} highcharts/yaxis/linecolor/
  16432. * A red line on Y axis
  16433. * @sample {highcharts|highstock} highcharts/css/axis/
  16434. * Axes in styled mode
  16435. * @sample {highstock} stock/xaxis/linecolor/
  16436. * A red line on X axis
  16437. *
  16438. * @type {Highcharts.ColorType}
  16439. */
  16440. lineColor: "#333333" /* Palette.neutralColor80 */,
  16441. /**
  16442. * The width of the line marking the axis itself.
  16443. *
  16444. * In styled mode, the stroke width is given in the
  16445. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  16446. *
  16447. * @sample {highcharts} highcharts/yaxis/linecolor/
  16448. * A 1px line on Y axis
  16449. * @sample {highcharts|highstock} highcharts/css/axis/
  16450. * Axes in styled mode
  16451. * @sample {highstock} stock/xaxis/linewidth/
  16452. * A 2px line on X axis
  16453. *
  16454. * @default {highcharts|highstock} 1
  16455. * @default {highmaps} 0
  16456. */
  16457. lineWidth: 1,
  16458. /**
  16459. * Color of the grid lines extending the ticks across the plot area.
  16460. *
  16461. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  16462. * class.
  16463. *
  16464. * @productdesc {highmaps}
  16465. * In Highmaps, the grid lines are hidden by default.
  16466. *
  16467. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  16468. * Green lines
  16469. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16470. * Styled mode
  16471. * @sample {highstock} stock/xaxis/gridlinecolor/
  16472. * Green lines
  16473. *
  16474. * @type {Highcharts.ColorType}
  16475. * @default #e6e6e6
  16476. */
  16477. gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
  16478. /**
  16479. * The width of the grid lines extending the ticks across the plot area.
  16480. * Defaults to 1 on the Y axis and 0 on the X axis, except for 3d
  16481. * charts.
  16482. *
  16483. * In styled mode, the stroke width is given in the
  16484. * `.highcharts-grid-line` class.
  16485. *
  16486. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  16487. * 2px lines
  16488. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16489. * Styled mode
  16490. * @sample {highstock} stock/xaxis/gridlinewidth/
  16491. * 2px lines
  16492. *
  16493. * @type {number}
  16494. * @apioption xAxis.gridLineWidth
  16495. */
  16496. gridLineWidth: void 0,
  16497. /**
  16498. * The height as the vertical axis. If it's a number, it is
  16499. * interpreted as pixels.
  16500. *
  16501. * Since Highcharts 2: If it's a percentage string, it is interpreted
  16502. * as percentages of the total plot height.
  16503. *
  16504. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  16505. * Different axis position properties
  16506. *
  16507. * @type {number|string}
  16508. * @product highcharts highstock
  16509. * @apioption xAxis.height
  16510. */
  16511. /**
  16512. * The width as the horizontal axis. If it's a number, it is interpreted
  16513. * as pixels.
  16514. *
  16515. * Since Highcharts v5.0.13: If it's a percentage string, it is
  16516. * interpreted as percentages of the total plot width.
  16517. *
  16518. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  16519. * Different axis position properties
  16520. *
  16521. * @type {number|string}
  16522. * @product highcharts highstock
  16523. * @apioption xAxis.width
  16524. */
  16525. /**
  16526. * Color for the main tick marks.
  16527. *
  16528. * In styled mode, the stroke is given in the `.highcharts-tick`
  16529. * class.
  16530. *
  16531. * @sample {highcharts} highcharts/xaxis/tickcolor/
  16532. * Red ticks on X axis
  16533. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16534. * Styled mode
  16535. * @sample {highstock} stock/xaxis/ticks/
  16536. * Formatted ticks on X axis
  16537. *
  16538. * @type {Highcharts.ColorType}
  16539. */
  16540. tickColor: "#333333" /* Palette.neutralColor80 */
  16541. // tickWidth: 1
  16542. };
  16543. /**
  16544. * The Y axis or value axis. Normally this is the vertical axis,
  16545. * though if the chart is inverted this is the horizontal axis.
  16546. * In case of multiple axes, the yAxis node is an array of
  16547. * configuration objects.
  16548. *
  16549. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  16550. * access to the axis.
  16551. *
  16552. * @type {*|Array<*>}
  16553. * @extends xAxis
  16554. * @excluding currentDateIndicator,ordinal,overscroll
  16555. * @optionparent yAxis
  16556. */
  16557. AxisDefaults.defaultYAxisOptions = {
  16558. /**
  16559. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  16560. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  16561. * `linear` for other chart types.
  16562. *
  16563. * In a datetime axis, the numbers are given in milliseconds, and tick
  16564. * marks are placed on appropriate values, like full hours or days. In a
  16565. * category or treegrid axis, the [point names](#series.line.data.name)
  16566. * of the chart's series are used for categories, if a
  16567. * [categories](#xAxis.categories) array is not defined.
  16568. *
  16569. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  16570. * Logarithmic with minor grid lines
  16571. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  16572. * Logarithmic with extension to emulate negative values
  16573. * @sample {gantt} gantt/treegrid-axis/demo
  16574. * Treegrid axis
  16575. *
  16576. * @type {Highcharts.AxisTypeValue}
  16577. * @default {highcharts} linear
  16578. * @default {gantt} treegrid
  16579. * @product highcharts gantt
  16580. * @apioption yAxis.type
  16581. */
  16582. /**
  16583. * The height of the Y axis. If it's a number, it is interpreted as
  16584. * pixels.
  16585. *
  16586. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  16587. * percentages of the total plot height.
  16588. *
  16589. * @see [yAxis.top](#yAxis.top)
  16590. *
  16591. * @sample {highstock} stock/demo/candlestick-and-volume/
  16592. * Percentage height panes
  16593. *
  16594. * @type {number|string}
  16595. * @product highcharts highstock
  16596. * @apioption yAxis.height
  16597. */
  16598. /**
  16599. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  16600. * to represent the maximum value of the Y axis.
  16601. *
  16602. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  16603. * Min and max colors
  16604. *
  16605. * @type {Highcharts.ColorType}
  16606. * @default #003399
  16607. * @since 4.0
  16608. * @product highcharts
  16609. * @apioption yAxis.maxColor
  16610. */
  16611. /**
  16612. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  16613. * to represent the minimum value of the Y axis.
  16614. *
  16615. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  16616. * Min and max color
  16617. *
  16618. * @type {Highcharts.ColorType}
  16619. * @default #e6ebf5
  16620. * @since 4.0
  16621. * @product highcharts
  16622. * @apioption yAxis.minColor
  16623. */
  16624. /**
  16625. * Whether to reverse the axis so that the highest number is closest
  16626. * to the origin.
  16627. *
  16628. * @sample {highcharts} highcharts/yaxis/reversed/
  16629. * Reversed Y axis
  16630. * @sample {highstock} stock/xaxis/reversed/
  16631. * Reversed Y axis
  16632. *
  16633. * @type {boolean}
  16634. * @default {highcharts} false
  16635. * @default {highstock} false
  16636. * @default {highmaps} true
  16637. * @default {gantt} true
  16638. * @apioption yAxis.reversed
  16639. */
  16640. /**
  16641. * If `true`, the first series in a stack will be drawn on top in a
  16642. * positive, non-reversed Y axis. If `false`, the first series is in
  16643. * the base of the stack.
  16644. *
  16645. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  16646. * Non-reversed stacks
  16647. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  16648. * Non-reversed stacks
  16649. *
  16650. * @type {boolean}
  16651. * @default true
  16652. * @since 3.0.10
  16653. * @product highcharts highstock
  16654. * @apioption yAxis.reversedStacks
  16655. */
  16656. reversedStacks: true,
  16657. /**
  16658. * Solid gauge series only. Color stops for the solid gauge. Use this
  16659. * in cases where a linear gradient between a `minColor` and `maxColor`
  16660. * is not sufficient. The stops is an array of tuples, where the first
  16661. * item is a float between 0 and 1 assigning the relative position in
  16662. * the gradient, and the second item is the color.
  16663. *
  16664. * For solid gauges, the Y axis also inherits the concept of
  16665. * [data classes](https://api.highcharts.com/highmaps#colorAxis.dataClasses)
  16666. * from the Highmaps color axis.
  16667. *
  16668. * @sample {highcharts} highcharts/demo/gauge-solid/
  16669. * Gauge with stops
  16670. *
  16671. * @see [minColor](#yAxis.minColor)
  16672. * @see [maxColor](#yAxis.maxColor)
  16673. *
  16674. * @type {Array<Array<number,Highcharts.ColorType>>}
  16675. * @since 4.0
  16676. * @product highcharts
  16677. * @apioption yAxis.stops
  16678. */
  16679. /**
  16680. * The pixel width of the major tick marks.
  16681. *
  16682. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  16683. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  16684. *
  16685. * @type {number}
  16686. * @default 0
  16687. * @product highcharts highstock gantt
  16688. * @apioption yAxis.tickWidth
  16689. */
  16690. /**
  16691. * Whether to force the axis to end on a tick. Use this option with
  16692. * the `maxPadding` option to control the axis end.
  16693. *
  16694. * This option is always disabled, when panning type is
  16695. * either `y` or `xy`.
  16696. *
  16697. * @see [type](#chart.panning.type)
  16698. *
  16699. *
  16700. * @sample {highcharts} highcharts/yaxis/endontick/
  16701. * True by default
  16702. * @sample {highcharts} highcharts/yaxis/endontick-false/
  16703. * False
  16704. * @sample {highstock} stock/demo/basic-line/
  16705. * True by default
  16706. * @sample {highstock} stock/xaxis/endontick/
  16707. * False for Y axis
  16708. *
  16709. * @since 1.2.0
  16710. */
  16711. endOnTick: true,
  16712. /**
  16713. * Padding of the max value relative to the length of the axis. A
  16714. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  16715. * when you don't want the highest data value to appear on the edge
  16716. * of the plot area. When the axis' `max` option is set or a max extreme
  16717. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  16718. *
  16719. * Also the `softThreshold` option takes precedence over `maxPadding`,
  16720. * so if the data is tangent to the threshold, `maxPadding` may not
  16721. * apply unless `softThreshold` is set to false.
  16722. *
  16723. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  16724. * Max padding of 0.2
  16725. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  16726. * Greater min- and maxPadding
  16727. *
  16728. * @since 1.2.0
  16729. * @product highcharts highstock gantt
  16730. */
  16731. maxPadding: 0.05,
  16732. /**
  16733. * Padding of the min value relative to the length of the axis. A
  16734. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  16735. * when you don't want the lowest data value to appear on the edge
  16736. * of the plot area. When the axis' `min` option is set or a max extreme
  16737. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  16738. *
  16739. * Also the `softThreshold` option takes precedence over `minPadding`,
  16740. * so if the data is tangent to the threshold, `minPadding` may not
  16741. * apply unless `softThreshold` is set to false.
  16742. *
  16743. * @sample {highcharts} highcharts/yaxis/minpadding/
  16744. * Min padding of 0.2
  16745. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  16746. * Greater min- and maxPadding
  16747. *
  16748. * @since 1.2.0
  16749. * @product highcharts highstock gantt
  16750. */
  16751. minPadding: 0.05,
  16752. /**
  16753. * @productdesc {highstock}
  16754. * In Highcharts Stock 1.x, the Y axis was placed
  16755. * on the left side by default.
  16756. *
  16757. * @sample {highcharts} highcharts/yaxis/opposite/
  16758. * Secondary Y axis opposite
  16759. * @sample {highstock} stock/xaxis/opposite/
  16760. * Y axis on left side
  16761. *
  16762. * @type {boolean}
  16763. * @default {highstock} true
  16764. * @default {highcharts} false
  16765. * @product highstock highcharts gantt
  16766. * @apioption yAxis.opposite
  16767. */
  16768. /**
  16769. * @see [tickInterval](#xAxis.tickInterval)
  16770. * @see [tickPositioner](#xAxis.tickPositioner)
  16771. * @see [tickPositions](#xAxis.tickPositions)
  16772. */
  16773. tickPixelInterval: 72,
  16774. /**
  16775. * Whether to show the last tick label.
  16776. *
  16777. * @productdesc {highcharts|gantt}
  16778. * Defaults to `true` on cartesian charts, and `false` on polar charts.
  16779. *
  16780. * @productdesc {highstock}
  16781. * Defaults to `true` for categorized yAxis and `false` for other types
  16782. * of yAxis.
  16783. *
  16784. * @default undefined
  16785. */
  16786. showLastLabel: true,
  16787. /**
  16788. * @extends xAxis.labels
  16789. */
  16790. labels: {
  16791. /**
  16792. * The label's pixel distance from the perimeter of the plot area.
  16793. * On cartesian charts, this is overridden if the `labels.y` setting
  16794. * is set.
  16795. *
  16796. * On polar charts, if it's a percentage string, it is interpreted
  16797. * the same as [series.radius](#plotOptions.gauge.radius), so the
  16798. * label can be aligned under the gauge's shape.
  16799. *
  16800. * @sample {highcharts} highcharts/yaxis/labels-distance/
  16801. * Polar chart, labels centered under the arc
  16802. *
  16803. * @type {number|string}
  16804. * @product highcharts
  16805. * @apioption yAxis.labels.distance
  16806. */
  16807. /**
  16808. * The y position offset of all labels relative to the tick
  16809. * positions on the axis. For polar and radial axis consider the use
  16810. * of the [distance](#yAxis.labels.distance) option.
  16811. *
  16812. * @sample {highcharts} highcharts/xaxis/labels-x/
  16813. * Y axis labels placed on grid lines
  16814. *
  16815. * @type {number}
  16816. * @default {highcharts} 3
  16817. * @default {highstock} -2
  16818. * @default {highmaps} 3
  16819. * @apioption yAxis.labels.y
  16820. */
  16821. /**
  16822. * What part of the string the given position is anchored to. Can
  16823. * be one of `"left"`, `"center"` or `"right"`. The exact position
  16824. * also depends on the `labels.x` setting.
  16825. *
  16826. * Angular gauges and solid gauges defaults to `"center"`.
  16827. * Solid gauges with two labels have additional option `"auto"`
  16828. * for automatic horizontal and vertical alignment.
  16829. *
  16830. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  16831. * Left
  16832. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  16833. * Solid gauge labels auto aligned
  16834. *
  16835. * @type {Highcharts.AlignValue}
  16836. * @default {highstock} right
  16837. * @apioption yAxis.labels.align
  16838. */
  16839. /**
  16840. * The x position offset of all labels relative to the tick
  16841. * positions on the axis. Defaults to -15 for left axis, 15 for
  16842. * right axis.
  16843. *
  16844. * @sample {highcharts} highcharts/xaxis/labels-x/
  16845. * Y axis labels placed on grid lines
  16846. *
  16847. * @type {number}
  16848. */
  16849. x: void 0
  16850. },
  16851. /**
  16852. * @sample {highcharts} highcharts/yaxis/max-200/
  16853. * Y axis max of 200
  16854. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  16855. * Y axis max on logarithmic axis
  16856. * @sample {highstock} stock/yaxis/min-max/
  16857. * Fixed min and max on Y axis
  16858. *
  16859. * @apioption yAxis.max
  16860. */
  16861. /**
  16862. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  16863. * -50 with startOnTick to false
  16864. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  16865. * -50 with startOnTick true by default
  16866. * @sample {highstock} stock/yaxis/min-max/
  16867. * Fixed min and max on Y axis
  16868. *
  16869. * @apioption yAxis.min
  16870. */
  16871. /**
  16872. * An optional scrollbar to display on the Y axis in response to
  16873. * limiting the minimum an maximum of the axis values.
  16874. *
  16875. * In styled mode, all the presentational options for the scrollbar
  16876. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  16877. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  16878. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  16879. *
  16880. * @sample {highstock} stock/yaxis/scrollbar/
  16881. * Scrollbar on the Y axis
  16882. *
  16883. * @extends scrollbar
  16884. * @since 4.2.6
  16885. * @product highstock
  16886. * @excluding height
  16887. * @apioption yAxis.scrollbar
  16888. */
  16889. /**
  16890. * Enable the scrollbar on the Y axis.
  16891. *
  16892. * @sample {highstock} stock/yaxis/scrollbar/
  16893. * Enabled on Y axis
  16894. *
  16895. * @type {boolean}
  16896. * @default false
  16897. * @since 4.2.6
  16898. * @product highstock
  16899. * @apioption yAxis.scrollbar.enabled
  16900. */
  16901. /**
  16902. * Pixel margin between the scrollbar and the axis elements.
  16903. *
  16904. * @type {number}
  16905. * @default 10
  16906. * @since 4.2.6
  16907. * @product highstock
  16908. * @apioption yAxis.scrollbar.margin
  16909. */
  16910. /* eslint-disable highcharts/doclet-apioption-last */
  16911. /**
  16912. * Defines the position of the scrollbar. By default, it is positioned
  16913. * on the opposite of the main axis (right side of the chart).
  16914. * However, in the case of RTL languages could be set to `false`
  16915. * which positions the scrollbar on the left.
  16916. *
  16917. * Works only for vertical axes.
  16918. * This means yAxis in a non-inverted chart and xAxis in the inverted.
  16919. *
  16920. * @sample stock/yaxis/scrollbar-opposite/
  16921. * A scrollbar not on the opposite side
  16922. *
  16923. * @type {boolean}
  16924. * @default true
  16925. * @since 9.3.0
  16926. *
  16927. * @apioption yAxis.scrollbar.opposite
  16928. * @apioption xAxis.scrollbar.opposite
  16929. *
  16930. */
  16931. /* eslint-enable highcharts/doclet-apioption-last */
  16932. /**
  16933. * Whether to show the scrollbar when it is fully zoomed out at max
  16934. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  16935. * hidden until the user zooms in, like common in browsers.
  16936. *
  16937. * @type {boolean}
  16938. * @default true
  16939. * @since 4.2.6
  16940. * @product highstock
  16941. * @apioption yAxis.scrollbar.showFull
  16942. */
  16943. /**
  16944. * The width of a vertical scrollbar or height of a horizontal
  16945. * scrollbar. Defaults to 20 on touch devices.
  16946. *
  16947. * @type {number}
  16948. * @default 14
  16949. * @since 4.2.6
  16950. * @product highstock
  16951. * @apioption yAxis.scrollbar.size
  16952. */
  16953. /**
  16954. * Z index of the scrollbar elements.
  16955. *
  16956. * @type {number}
  16957. * @default 3
  16958. * @since 4.2.6
  16959. * @product highstock
  16960. * @apioption yAxis.scrollbar.zIndex
  16961. */
  16962. /**
  16963. * A soft maximum for the axis. If the series data maximum is less
  16964. * than this, the axis will stay at this maximum, but if the series
  16965. * data maximum is higher, the axis will flex to show all data.
  16966. *
  16967. * **Note**: The [series.softThreshold](
  16968. * #plotOptions.series.softThreshold) option takes precedence over this
  16969. * option.
  16970. *
  16971. * @sample highcharts/yaxis/softmin-softmax/
  16972. * Soft min and max
  16973. *
  16974. * @type {number}
  16975. * @since 5.0.1
  16976. * @product highcharts highstock gantt
  16977. * @apioption yAxis.softMax
  16978. */
  16979. /**
  16980. * A soft minimum for the axis. If the series data minimum is greater
  16981. * than this, the axis will stay at this minimum, but if the series
  16982. * data minimum is lower, the axis will flex to show all data.
  16983. *
  16984. * **Note**: The [series.softThreshold](
  16985. * #plotOptions.series.softThreshold) option takes precedence over this
  16986. * option.
  16987. *
  16988. * @sample highcharts/yaxis/softmin-softmax/
  16989. * Soft min and max
  16990. *
  16991. * @type {number}
  16992. * @since 5.0.1
  16993. * @product highcharts highstock gantt
  16994. * @apioption yAxis.softMin
  16995. */
  16996. /**
  16997. * Defines the horizontal alignment of the stack total label. Can be one
  16998. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  16999. * at runtime and depends on orientation and whether the stack is
  17000. * positive or negative.
  17001. *
  17002. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  17003. * Aligned to the left
  17004. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  17005. * Aligned in center
  17006. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  17007. * Aligned to the right
  17008. *
  17009. * @type {Highcharts.AlignValue}
  17010. * @since 2.1.5
  17011. * @product highcharts
  17012. * @apioption yAxis.stackLabels.align
  17013. */
  17014. /**
  17015. * A format string for the data label. Available variables are the same
  17016. * as for `formatter`.
  17017. *
  17018. * @type {string}
  17019. * @default {total}
  17020. * @since 3.0.2
  17021. * @product highcharts highstock
  17022. * @apioption yAxis.stackLabels.format
  17023. */
  17024. /**
  17025. * Rotation of the labels in degrees.
  17026. *
  17027. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  17028. * Labels rotated 45°
  17029. *
  17030. * @type {number}
  17031. * @default 0
  17032. * @since 2.1.5
  17033. * @product highcharts
  17034. * @apioption yAxis.stackLabels.rotation
  17035. */
  17036. /**
  17037. * The text alignment for the label. While `align` determines where the
  17038. * texts anchor point is placed with regards to the stack, `textAlign`
  17039. * determines how the text is aligned against its anchor point. Possible
  17040. * values are `"left"`, `"center"` and `"right"`. The default value is
  17041. * calculated at runtime and depends on orientation and whether the
  17042. * stack is positive or negative.
  17043. *
  17044. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  17045. * Label in center position but text-aligned left
  17046. *
  17047. * @type {Highcharts.AlignValue}
  17048. * @since 2.1.5
  17049. * @product highcharts
  17050. * @apioption yAxis.stackLabels.textAlign
  17051. */
  17052. /**
  17053. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  17054. * to render the labels.
  17055. *
  17056. * @type {boolean}
  17057. * @default false
  17058. * @since 3.0
  17059. * @product highcharts highstock
  17060. * @apioption yAxis.stackLabels.useHTML
  17061. */
  17062. /**
  17063. * Defines the vertical alignment of the stack total label. Can be one
  17064. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  17065. * at runtime and depends on orientation and whether the stack is
  17066. * positive or negative.
  17067. *
  17068. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  17069. * Vertically aligned top
  17070. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  17071. * Vertically aligned middle
  17072. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  17073. * Vertically aligned bottom
  17074. *
  17075. * @type {Highcharts.VerticalAlignValue}
  17076. * @since 2.1.5
  17077. * @product highcharts
  17078. * @apioption yAxis.stackLabels.verticalAlign
  17079. */
  17080. /**
  17081. * The x position offset of the label relative to the left of the
  17082. * stacked bar. The default value is calculated at runtime and depends
  17083. * on orientation and whether the stack is positive or negative.
  17084. *
  17085. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  17086. * Stack total labels with x offset
  17087. *
  17088. * @type {number}
  17089. * @since 2.1.5
  17090. * @product highcharts
  17091. * @apioption yAxis.stackLabels.x
  17092. */
  17093. /**
  17094. * The y position offset of the label relative to the tick position
  17095. * on the axis. The default value is calculated at runtime and depends
  17096. * on orientation and whether the stack is positive or negative.
  17097. *
  17098. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  17099. * Stack total labels with y offset
  17100. *
  17101. * @type {number}
  17102. * @since 2.1.5
  17103. * @product highcharts
  17104. * @apioption yAxis.stackLabels.y
  17105. */
  17106. /**
  17107. * Whether to force the axis to start on a tick. Use this option with
  17108. * the `maxPadding` option to control the axis start.
  17109. *
  17110. * This option is always disabled, when panning type is
  17111. * either `y` or `xy`.
  17112. *
  17113. * @see [type](#chart.panning.type)
  17114. *
  17115. * @sample {highcharts} highcharts/xaxis/startontick-false/
  17116. * False by default
  17117. * @sample {highcharts} highcharts/xaxis/startontick-true/
  17118. * True
  17119. * @sample {highstock} stock/xaxis/endontick/
  17120. * False for Y axis
  17121. *
  17122. * @since 1.2.0
  17123. * @product highcharts highstock gantt
  17124. */
  17125. startOnTick: true,
  17126. title: {
  17127. /**
  17128. * The pixel distance between the axis labels and the title.
  17129. * Positive values are outside the axis line, negative are inside.
  17130. *
  17131. * @sample {highcharts} highcharts/xaxis/title-margin/
  17132. * Y axis title margin of 60
  17133. *
  17134. * @type {number}
  17135. * @default 40
  17136. * @apioption yAxis.title.margin
  17137. */
  17138. /**
  17139. * The rotation of the text in degrees. 0 is horizontal, 270 is
  17140. * vertical reading from bottom to top.
  17141. *
  17142. * @sample {highcharts} highcharts/yaxis/title-offset/
  17143. * Horizontal
  17144. */
  17145. rotation: 270,
  17146. /**
  17147. * The actual text of the axis title. Horizontal texts can contain
  17148. * HTML, but rotated texts are painted using vector techniques and
  17149. * must be clean text. The Y axis title is disabled by setting the
  17150. * `text` option to `undefined`.
  17151. *
  17152. * @sample {highcharts} highcharts/xaxis/title-text/
  17153. * Custom HTML
  17154. *
  17155. * @type {string|null}
  17156. * @default {highcharts} Values
  17157. * @default {highstock} undefined
  17158. * @product highcharts highstock gantt
  17159. */
  17160. text: 'Values'
  17161. },
  17162. /**
  17163. * The top position of the Y axis. If it's a number, it is interpreted
  17164. * as pixel position relative to the chart.
  17165. *
  17166. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  17167. * percentages of the plot height, offset from plot area top.
  17168. *
  17169. * @see [yAxis.height](#yAxis.height)
  17170. *
  17171. * @sample {highstock} stock/demo/candlestick-and-volume/
  17172. * Percentage height panes
  17173. *
  17174. * @type {number|string}
  17175. * @product highcharts highstock
  17176. * @apioption yAxis.top
  17177. */
  17178. /**
  17179. * The stack labels show the total value for each bar in a stacked
  17180. * column or bar chart. The label will be placed on top of positive
  17181. * columns and below negative columns. In case of an inverted column
  17182. * chart or a bar chart the label is placed to the right of positive
  17183. * bars and to the left of negative bars.
  17184. *
  17185. * @product highcharts
  17186. */
  17187. stackLabels: {
  17188. /**
  17189. * Enable or disable the initial animation when a series is
  17190. * displayed for the `stackLabels`. The animation can also be set as
  17191. * a configuration object. Please note that this option only
  17192. * applies to the initial animation.
  17193. * For other animations, see [chart.animation](#chart.animation)
  17194. * and the animation parameter under the API methods.
  17195. * The following properties are supported:
  17196. *
  17197. * - `defer`: The animation delay time in milliseconds.
  17198. *
  17199. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  17200. * Animation defer settings
  17201. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  17202. * @since 8.2.0
  17203. * @apioption yAxis.stackLabels.animation
  17204. */
  17205. animation: {},
  17206. /**
  17207. * The animation delay time in milliseconds.
  17208. * Set to `0` renders stackLabel immediately.
  17209. * As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer).
  17210. *
  17211. * @type {number}
  17212. * @since 8.2.0
  17213. * @apioption yAxis.stackLabels.animation.defer
  17214. */
  17215. /**
  17216. * Allow the stack labels to overlap.
  17217. *
  17218. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  17219. * Default false
  17220. *
  17221. * @since 5.0.13
  17222. * @product highcharts
  17223. */
  17224. allowOverlap: false,
  17225. /**
  17226. * The background color or gradient for the stack label.
  17227. *
  17228. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17229. * Stack labels box options
  17230. * @type {Highcharts.ColorType}
  17231. * @since 8.1.0
  17232. * @apioption yAxis.stackLabels.backgroundColor
  17233. */
  17234. /**
  17235. * The border color for the stack label. Defaults to `undefined`.
  17236. *
  17237. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17238. * Stack labels box options
  17239. * @type {Highcharts.ColorType}
  17240. * @since 8.1.0
  17241. * @apioption yAxis.stackLabels.borderColor
  17242. */
  17243. /**
  17244. * The border radius in pixels for the stack label.
  17245. *
  17246. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17247. * Stack labels box options
  17248. * @type {number}
  17249. * @default 0
  17250. * @since 8.1.0
  17251. * @apioption yAxis.stackLabels.borderRadius
  17252. */
  17253. /**
  17254. * The border width in pixels for the stack label.
  17255. *
  17256. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17257. * Stack labels box options
  17258. * @type {number}
  17259. * @default 0
  17260. * @since 8.1.0
  17261. * @apioption yAxis.stackLabels.borderWidth
  17262. */
  17263. /**
  17264. * Enable or disable the stack total labels.
  17265. *
  17266. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  17267. * Enabled stack total labels
  17268. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
  17269. * Enabled stack labels in waterfall chart
  17270. *
  17271. * @since 2.1.5
  17272. * @product highcharts
  17273. */
  17274. enabled: false,
  17275. /**
  17276. * Whether to hide stack labels that are outside the plot area.
  17277. * By default, the stack label is moved
  17278. * inside the plot area according to the
  17279. * [overflow](/highcharts/#yAxis/stackLabels/overflow)
  17280. * option.
  17281. *
  17282. * @type {boolean}
  17283. * @since 7.1.3
  17284. */
  17285. crop: true,
  17286. /**
  17287. * How to handle stack total labels that flow outside the plot area.
  17288. * The default is set to `"justify"`,
  17289. * which aligns them inside the plot area.
  17290. * For columns and bars, this means it will be moved inside the bar.
  17291. * To display stack labels outside the plot area,
  17292. * set `crop` to `false` and `overflow` to `"allow"`.
  17293. *
  17294. * @sample highcharts/yaxis/stacklabels-overflow/
  17295. * Stack labels flows outside the plot area.
  17296. *
  17297. * @type {Highcharts.DataLabelsOverflowValue}
  17298. * @since 7.1.3
  17299. */
  17300. overflow: 'justify',
  17301. /* eslint-disable valid-jsdoc */
  17302. /**
  17303. * Callback JavaScript function to format the label. The value is
  17304. * given by `this.total`.
  17305. *
  17306. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  17307. * Added units to stack total value
  17308. *
  17309. * @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
  17310. * @since 2.1.5
  17311. * @product highcharts
  17312. */
  17313. formatter: function () {
  17314. const { numberFormatter } = this.axis.chart;
  17315. /* eslint-enable valid-jsdoc */
  17316. return numberFormatter(this.total || 0, -1);
  17317. },
  17318. /**
  17319. * CSS styles for the label.
  17320. *
  17321. * In styled mode, the styles are set in the
  17322. * `.highcharts-stack-label` class.
  17323. *
  17324. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  17325. * Red stack total labels
  17326. *
  17327. * @type {Highcharts.CSSObject}
  17328. * @since 2.1.5
  17329. * @product highcharts
  17330. */
  17331. style: {
  17332. /** @internal */
  17333. color: "#000000" /* Palette.neutralColor100 */,
  17334. /** @internal */
  17335. fontSize: '0.7em',
  17336. /** @internal */
  17337. fontWeight: 'bold',
  17338. /** @internal */
  17339. textOutline: '1px contrast'
  17340. }
  17341. },
  17342. gridLineWidth: 1,
  17343. lineWidth: 0
  17344. // tickWidth: 0
  17345. };
  17346. /**
  17347. * The Z axis or depth axis for 3D plots.
  17348. *
  17349. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  17350. * access to the axis.
  17351. *
  17352. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  17353. * Z-Axis with Categories
  17354. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  17355. * Z-Axis with styling
  17356. *
  17357. * @type {*|Array<*>}
  17358. * @extends xAxis
  17359. * @since 5.0.0
  17360. * @product highcharts
  17361. * @excluding breaks, crosshair, height, left, lineColor, lineWidth,
  17362. * nameToX, showEmpty, top, width
  17363. * @apioption zAxis
  17364. */
  17365. // This variable extends the defaultOptions for left axes.
  17366. AxisDefaults.defaultLeftAxisOptions = {
  17367. title: {
  17368. rotation: 270
  17369. }
  17370. };
  17371. // This variable extends the defaultOptions for right axes.
  17372. AxisDefaults.defaultRightAxisOptions = {
  17373. title: {
  17374. rotation: 90
  17375. }
  17376. };
  17377. // This variable extends the defaultOptions for bottom axes.
  17378. AxisDefaults.defaultBottomAxisOptions = {
  17379. labels: {
  17380. autoRotation: [-45]
  17381. // overflow: undefined,
  17382. // staggerLines: null
  17383. },
  17384. margin: 15,
  17385. title: {
  17386. rotation: 0
  17387. }
  17388. };
  17389. // This variable extends the defaultOptions for top axes.
  17390. AxisDefaults.defaultTopAxisOptions = {
  17391. labels: {
  17392. autoRotation: [-45]
  17393. // overflow: undefined
  17394. // staggerLines: null
  17395. },
  17396. margin: 15,
  17397. title: {
  17398. rotation: 0
  17399. }
  17400. };
  17401. })(AxisDefaults || (AxisDefaults = {}));
  17402. /* *
  17403. *
  17404. * Default Export
  17405. *
  17406. * */
  17407. return AxisDefaults;
  17408. });
  17409. _registerModule(_modules, 'Core/Foundation.js', [_modules['Core/Utilities.js']], function (U) {
  17410. /* *
  17411. *
  17412. * (c) 2010-2021 Torstein Honsi
  17413. *
  17414. * License: www.highcharts.com/license
  17415. *
  17416. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  17417. *
  17418. * */
  17419. const { addEvent, isFunction, objectEach, removeEvent } = U;
  17420. /* *
  17421. *
  17422. * Class Namespace
  17423. *
  17424. * */
  17425. var Foundation;
  17426. (function (Foundation) {
  17427. /* *
  17428. *
  17429. * Functions
  17430. *
  17431. * */
  17432. /* eslint-disable valid-jsdoc */
  17433. /**
  17434. * Register event options. If an event handler is set on the options, it
  17435. * should be subject to Chart.update, Axis.update and Series.update. This is
  17436. * contrary to general handlers that are set directly using addEvent either
  17437. * on the class or on the instance. #6538, #6943, #10861.
  17438. * @private
  17439. */
  17440. function registerEventOptions(component, options) {
  17441. // A lookup over those events that are added by _options_ (not
  17442. // programmatically). These are updated through .update()
  17443. component.eventOptions = component.eventOptions || {};
  17444. // Register event listeners
  17445. objectEach(options.events, function (event, eventType) {
  17446. // If event does not exist, or is changed by the .update()
  17447. // function
  17448. if (component.eventOptions[eventType] !== event) {
  17449. // Remove existing if set by option
  17450. if (component.eventOptions[eventType]) {
  17451. removeEvent(component, eventType, component.eventOptions[eventType]);
  17452. delete component.eventOptions[eventType];
  17453. }
  17454. if (isFunction(event)) {
  17455. component.eventOptions[eventType] = event;
  17456. addEvent(component, eventType, event, {
  17457. order: 0 // #14080 fire those events as firsts
  17458. });
  17459. }
  17460. }
  17461. });
  17462. }
  17463. Foundation.registerEventOptions = registerEventOptions;
  17464. })(Foundation || (Foundation = {}));
  17465. /* *
  17466. *
  17467. * Default Export
  17468. *
  17469. * */
  17470. return Foundation;
  17471. });
  17472. _registerModule(_modules, 'Core/Axis/Tick.js', [_modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (F, H, U) {
  17473. /* *
  17474. *
  17475. * (c) 2010-2021 Torstein Honsi
  17476. *
  17477. * License: www.highcharts.com/license
  17478. *
  17479. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  17480. *
  17481. * */
  17482. const { deg2rad } = H;
  17483. const { clamp, correctFloat, defined, destroyObjectProperties, extend, fireEvent, isNumber, merge, objectEach, pick } = U;
  17484. /* *
  17485. *
  17486. * Class
  17487. *
  17488. * */
  17489. /* eslint-disable no-invalid-this, valid-jsdoc */
  17490. /**
  17491. * The Tick class.
  17492. *
  17493. * @class
  17494. * @name Highcharts.Tick
  17495. *
  17496. * @param {Highcharts.Axis} axis
  17497. * The axis of the tick.
  17498. *
  17499. * @param {number} pos
  17500. * The position of the tick on the axis in terms of axis values.
  17501. *
  17502. * @param {string} [type]
  17503. * The type of tick, either 'minor' or an empty string
  17504. *
  17505. * @param {boolean} [noLabel=false]
  17506. * Whether to disable the label or not. Defaults to false.
  17507. *
  17508. * @param {Object} [parameters]
  17509. * Optional parameters for the tick.
  17510. */
  17511. class Tick {
  17512. /* *
  17513. *
  17514. * Constructors
  17515. *
  17516. * */
  17517. constructor(axis, pos, type, noLabel, parameters) {
  17518. this.isNew = true;
  17519. this.isNewLabel = true;
  17520. /**
  17521. * The related axis of the tick.
  17522. * @name Highcharts.Tick#axis
  17523. * @type {Highcharts.Axis}
  17524. */
  17525. this.axis = axis;
  17526. /**
  17527. * The logical position of the tick on the axis in terms of axis values.
  17528. * @name Highcharts.Tick#pos
  17529. * @type {number}
  17530. */
  17531. this.pos = pos;
  17532. /**
  17533. * The tick type, which can be `"minor"`, or an empty string.
  17534. * @name Highcharts.Tick#type
  17535. * @type {string}
  17536. */
  17537. this.type = type || '';
  17538. this.parameters = parameters || {};
  17539. /**
  17540. * The mark offset of the tick on the axis. Usually `undefined`, numeric
  17541. * for grid axes.
  17542. * @name Highcharts.Tick#tickmarkOffset
  17543. * @type {number|undefined}
  17544. */
  17545. this.tickmarkOffset = this.parameters.tickmarkOffset;
  17546. this.options = this.parameters.options;
  17547. fireEvent(this, 'init');
  17548. if (!type && !noLabel) {
  17549. this.addLabel();
  17550. }
  17551. }
  17552. /* *
  17553. *
  17554. * Functions
  17555. *
  17556. * */
  17557. /**
  17558. * Write the tick label.
  17559. *
  17560. * @private
  17561. * @function Highcharts.Tick#addLabel
  17562. */
  17563. addLabel() {
  17564. const tick = this, axis = tick.axis, options = axis.options, chart = axis.chart, categories = axis.categories, log = axis.logarithmic, names = axis.names, pos = tick.pos, labelOptions = pick(tick.options && tick.options.labels, options.labels), tickPositions = axis.tickPositions, isFirst = pos === tickPositions[0], isLast = pos === tickPositions[tickPositions.length - 1], animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
  17565. axis.tickInterval === 1, tickPositionInfo = tickPositions.info;
  17566. let label = tick.label, dateTimeLabelFormat, dateTimeLabelFormats, i;
  17567. // The context value
  17568. let value = this.parameters.category || (categories ?
  17569. pick(categories[pos], names[pos], pos) :
  17570. pos);
  17571. if (log && isNumber(value)) {
  17572. value = correctFloat(log.lin2log(value));
  17573. }
  17574. // Set the datetime label format. If a higher rank is set for this
  17575. // position, use that. If not, use the general format.
  17576. if (axis.dateTime) {
  17577. if (tickPositionInfo) {
  17578. dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
  17579. tickPositionInfo.higherRanks[pos]) ||
  17580. tickPositionInfo.unitName]);
  17581. dateTimeLabelFormat = dateTimeLabelFormats.main;
  17582. }
  17583. else if (isNumber(value)) { // #1441
  17584. dateTimeLabelFormat = axis.dateTime.getXDateFormat(value, options.dateTimeLabelFormats ||
  17585. {});
  17586. }
  17587. }
  17588. // set properties for access in render method
  17589. /**
  17590. * True if the tick is the first one on the axis.
  17591. * @name Highcharts.Tick#isFirst
  17592. * @readonly
  17593. * @type {boolean|undefined}
  17594. */
  17595. tick.isFirst = isFirst;
  17596. /**
  17597. * True if the tick is the last one on the axis.
  17598. * @name Highcharts.Tick#isLast
  17599. * @readonly
  17600. * @type {boolean|undefined}
  17601. */
  17602. tick.isLast = isLast;
  17603. // Get the string
  17604. const ctx = {
  17605. axis,
  17606. chart,
  17607. dateTimeLabelFormat: dateTimeLabelFormat,
  17608. isFirst,
  17609. isLast,
  17610. pos,
  17611. tick: tick,
  17612. tickPositionInfo,
  17613. value
  17614. };
  17615. // Fire an event that allows modifying the context for use in
  17616. // `labels.format` and `labels.formatter`.
  17617. fireEvent(this, 'labelFormat', ctx);
  17618. // Label formatting. When `labels.format` is given, we first run the
  17619. // defaultFormatter and append the result to the context as `text`.
  17620. // Handy for adding prefix or suffix while keeping default number
  17621. // formatting.
  17622. const labelFormatter = (ctx) => {
  17623. if (labelOptions.formatter) {
  17624. return labelOptions.formatter.call(ctx, ctx);
  17625. }
  17626. if (labelOptions.format) {
  17627. ctx.text = axis.defaultLabelFormatter.call(ctx, ctx);
  17628. return F.format(labelOptions.format, ctx, chart);
  17629. }
  17630. return axis.defaultLabelFormatter.call(ctx, ctx);
  17631. };
  17632. const str = labelFormatter.call(ctx, ctx);
  17633. // Set up conditional formatting based on the format list if existing.
  17634. const list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  17635. if (list) {
  17636. tick.shortenLabel = function () {
  17637. for (i = 0; i < list.length; i++) {
  17638. extend(ctx, { dateTimeLabelFormat: list[i] });
  17639. label.attr({
  17640. text: labelFormatter.call(ctx, ctx)
  17641. });
  17642. if (label.getBBox().width <
  17643. axis.getSlotWidth(tick) - 2 *
  17644. labelOptions.padding) {
  17645. return;
  17646. }
  17647. }
  17648. label.attr({
  17649. text: ''
  17650. });
  17651. };
  17652. }
  17653. else {
  17654. // #15692
  17655. tick.shortenLabel = void 0;
  17656. }
  17657. // Call only after first render
  17658. if (animateLabels && axis._addedPlotLB) {
  17659. tick.moveLabel(str, labelOptions);
  17660. }
  17661. // First call
  17662. if (!defined(label) && !tick.movedLabel) {
  17663. /**
  17664. * The rendered text label of the tick.
  17665. * @name Highcharts.Tick#label
  17666. * @type {Highcharts.SVGElement|undefined}
  17667. */
  17668. tick.label = label = tick.createLabel({ x: 0, y: 0 }, str, labelOptions);
  17669. // Base value to detect change for new calls to getBBox
  17670. tick.rotation = 0;
  17671. // update
  17672. }
  17673. else if (label && label.textStr !== str && !animateLabels) {
  17674. // When resetting text, also reset the width if dynamically set
  17675. // (#8809)
  17676. if (label.textWidth &&
  17677. !labelOptions.style.width &&
  17678. !label.styles.width) {
  17679. label.css({ width: null });
  17680. }
  17681. label.attr({ text: str });
  17682. label.textPxLength = label.getBBox().width;
  17683. }
  17684. }
  17685. /**
  17686. * Render and return the label of the tick.
  17687. *
  17688. * @private
  17689. * @function Highcharts.Tick#createLabel
  17690. */
  17691. createLabel(xy, str, labelOptions) {
  17692. const axis = this.axis, chart = axis.chart, label = defined(str) && labelOptions.enabled ?
  17693. chart.renderer
  17694. .text(str, xy.x, xy.y, labelOptions.useHTML)
  17695. .add(axis.labelGroup) :
  17696. null;
  17697. // Un-rotated length
  17698. if (label) {
  17699. // Without position absolute, IE export sometimes is wrong
  17700. if (!chart.styledMode) {
  17701. label.css(merge(labelOptions.style));
  17702. }
  17703. label.textPxLength = label.getBBox().width;
  17704. }
  17705. return label;
  17706. }
  17707. /**
  17708. * Destructor for the tick prototype
  17709. *
  17710. * @private
  17711. * @function Highcharts.Tick#destroy
  17712. */
  17713. destroy() {
  17714. destroyObjectProperties(this, this.axis);
  17715. }
  17716. /**
  17717. * Gets the x and y positions for ticks in terms of pixels.
  17718. *
  17719. * @private
  17720. * @function Highcharts.Tick#getPosition
  17721. *
  17722. * @param {boolean} horiz
  17723. * Whether the tick is on an horizontal axis or not.
  17724. *
  17725. * @param {number} tickPos
  17726. * Position of the tick.
  17727. *
  17728. * @param {number} tickmarkOffset
  17729. * Tickmark offset for all ticks.
  17730. *
  17731. * @param {boolean} [old]
  17732. * Whether the axis has changed or not.
  17733. *
  17734. * @return {Highcharts.PositionObject}
  17735. * The tick position.
  17736. *
  17737. * @emits Highcharts.Tick#event:afterGetPosition
  17738. */
  17739. getPosition(horiz, tickPos, tickmarkOffset, old) {
  17740. const axis = this.axis, chart = axis.chart, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, pos = {
  17741. x: horiz ?
  17742. correctFloat(axis.translate(tickPos + tickmarkOffset, void 0, void 0, old) +
  17743. axis.transB) :
  17744. (axis.left +
  17745. axis.offset +
  17746. (axis.opposite ?
  17747. (((old && chart.oldChartWidth) ||
  17748. chart.chartWidth) -
  17749. axis.right -
  17750. axis.left) :
  17751. 0)),
  17752. y: horiz ?
  17753. (cHeight -
  17754. axis.bottom +
  17755. axis.offset -
  17756. (axis.opposite ? axis.height : 0)) :
  17757. correctFloat(cHeight -
  17758. axis.translate(tickPos + tickmarkOffset, void 0, void 0, old) -
  17759. axis.transB)
  17760. };
  17761. // Chrome workaround for #10516
  17762. pos.y = clamp(pos.y, -1e5, 1e5);
  17763. fireEvent(this, 'afterGetPosition', { pos: pos });
  17764. return pos;
  17765. }
  17766. /**
  17767. * Get the x, y position of the tick label
  17768. * @private
  17769. */
  17770. getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  17771. const axis = this.axis, transA = axis.transA, reversed = ( // #7911
  17772. axis.isLinked && axis.linkedParent ?
  17773. axis.linkedParent.reversed :
  17774. axis.reversed), staggerLines = axis.staggerLines, rotCorr = axis.tickRotCorr || { x: 0, y: 0 },
  17775. // Adjust for label alignment if we use reserveSpace: true (#5286)
  17776. labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
  17777. -axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
  17778. 0), distance = labelOptions.distance, pos = {};
  17779. let yOffset, line;
  17780. if (axis.side === 0) {
  17781. yOffset = label.rotation ? -distance : -label.getBBox().height;
  17782. }
  17783. else if (axis.side === 2) {
  17784. yOffset = rotCorr.y + distance;
  17785. }
  17786. else {
  17787. // #3140, #3140
  17788. yOffset = Math.cos(label.rotation * deg2rad) *
  17789. (rotCorr.y - label.getBBox(false, 0).height / 2);
  17790. }
  17791. if (defined(labelOptions.y)) {
  17792. yOffset = axis.side === 0 && axis.horiz ?
  17793. labelOptions.y + yOffset :
  17794. labelOptions.y;
  17795. }
  17796. x = x +
  17797. pick(labelOptions.x, [0, 1, 0, -1][axis.side] * distance) +
  17798. labelOffsetCorrection +
  17799. rotCorr.x -
  17800. (tickmarkOffset && horiz ?
  17801. tickmarkOffset * transA * (reversed ? -1 : 1) :
  17802. 0);
  17803. y = y + yOffset - (tickmarkOffset && !horiz ?
  17804. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  17805. // Correct for staggered labels
  17806. if (staggerLines) {
  17807. line = (index / (step || 1) % staggerLines);
  17808. if (axis.opposite) {
  17809. line = staggerLines - line - 1;
  17810. }
  17811. y += line * (axis.labelOffset / staggerLines);
  17812. }
  17813. pos.x = x;
  17814. pos.y = Math.round(y);
  17815. fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
  17816. return pos;
  17817. }
  17818. /**
  17819. * Get the offset height or width of the label
  17820. *
  17821. * @private
  17822. * @function Highcharts.Tick#getLabelSize
  17823. */
  17824. getLabelSize() {
  17825. return this.label ?
  17826. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  17827. 0;
  17828. }
  17829. /**
  17830. * Extendible method to return the path of the marker
  17831. * @private
  17832. */
  17833. getMarkPath(x, y, tickLength, tickWidth, horiz, renderer) {
  17834. return renderer.crispLine([[
  17835. 'M',
  17836. x,
  17837. y
  17838. ], [
  17839. 'L',
  17840. x + (horiz ? 0 : -tickLength),
  17841. y + (horiz ? tickLength : 0)
  17842. ]], tickWidth);
  17843. }
  17844. /**
  17845. * Handle the label overflow by adjusting the labels to the left and right
  17846. * edge, or hide them if they collide into the neighbour label.
  17847. *
  17848. * @private
  17849. * @function Highcharts.Tick#handleOverflow
  17850. */
  17851. handleOverflow(xy) {
  17852. const tick = this, axis = this.axis, labelOptions = axis.options.labels, pxPos = xy.x, chartWidth = axis.chart.chartWidth, spacing = axis.chart.spacing, leftBound = pick(axis.labelLeft, Math.min(axis.pos, spacing[3])), rightBound = pick(axis.labelRight, Math.max(!axis.isRadial ? axis.pos + axis.len : 0, chartWidth - spacing[1])), label = this.label, rotation = this.rotation, factor = {
  17853. left: 0,
  17854. center: 0.5,
  17855. right: 1
  17856. }[axis.labelAlign || label.attr('align')], labelWidth = label.getBBox().width, slotWidth = axis.getSlotWidth(tick), xCorrection = factor, css = {};
  17857. let modifiedSlotWidth = slotWidth, goRight = 1, leftPos, rightPos, textWidth;
  17858. // Check if the label overshoots the chart spacing box. If it does, move
  17859. // it. If it now overshoots the slotWidth, add ellipsis.
  17860. if (!rotation && labelOptions.overflow === 'justify') {
  17861. leftPos = pxPos - factor * labelWidth;
  17862. rightPos = pxPos + (1 - factor) * labelWidth;
  17863. if (leftPos < leftBound) {
  17864. modifiedSlotWidth =
  17865. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  17866. }
  17867. else if (rightPos > rightBound) {
  17868. modifiedSlotWidth =
  17869. rightBound - xy.x + modifiedSlotWidth * factor;
  17870. goRight = -1;
  17871. }
  17872. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  17873. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  17874. xy.x += (goRight *
  17875. (slotWidth -
  17876. modifiedSlotWidth -
  17877. xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
  17878. }
  17879. // If the label width exceeds the available space, set a text width
  17880. // to be picked up below. Also, if a width has been set before, we
  17881. // need to set a new one because the reported labelWidth will be
  17882. // limited by the box (#3938).
  17883. if (labelWidth > modifiedSlotWidth ||
  17884. (axis.autoRotation && (label.styles || {}).width)) {
  17885. textWidth = modifiedSlotWidth;
  17886. }
  17887. // Add ellipsis to prevent rotated labels to be clipped against the edge
  17888. // of the chart
  17889. }
  17890. else if (rotation < 0 &&
  17891. pxPos - factor * labelWidth < leftBound) {
  17892. textWidth = Math.round(pxPos / Math.cos(rotation * deg2rad) - leftBound);
  17893. }
  17894. else if (rotation > 0 &&
  17895. pxPos + factor * labelWidth > rightBound) {
  17896. textWidth = Math.round((chartWidth - pxPos) /
  17897. Math.cos(rotation * deg2rad));
  17898. }
  17899. if (textWidth) {
  17900. if (tick.shortenLabel) {
  17901. tick.shortenLabel();
  17902. }
  17903. else {
  17904. css.width = Math.floor(textWidth) + 'px';
  17905. if (!(labelOptions.style || {}).textOverflow) {
  17906. css.textOverflow = 'ellipsis';
  17907. }
  17908. label.css(css);
  17909. }
  17910. }
  17911. }
  17912. /**
  17913. * Try to replace the label if the same one already exists.
  17914. *
  17915. * @private
  17916. * @function Highcharts.Tick#moveLabel
  17917. */
  17918. moveLabel(str, labelOptions) {
  17919. const tick = this, label = tick.label, axis = tick.axis;
  17920. let moved = false, labelPos;
  17921. if (label && label.textStr === str) {
  17922. tick.movedLabel = label;
  17923. moved = true;
  17924. delete tick.label;
  17925. }
  17926. else { // Find a label with the same string
  17927. objectEach(axis.ticks, function (currentTick) {
  17928. if (!moved &&
  17929. !currentTick.isNew &&
  17930. currentTick !== tick &&
  17931. currentTick.label &&
  17932. currentTick.label.textStr === str) {
  17933. tick.movedLabel = currentTick.label;
  17934. moved = true;
  17935. currentTick.labelPos = tick.movedLabel.xy;
  17936. delete currentTick.label;
  17937. }
  17938. });
  17939. }
  17940. // Create new label if the actual one is moved
  17941. if (!moved && (tick.labelPos || label)) {
  17942. labelPos = tick.labelPos || label.xy;
  17943. tick.movedLabel = tick.createLabel(labelPos, str, labelOptions);
  17944. if (tick.movedLabel) {
  17945. tick.movedLabel.attr({ opacity: 0 });
  17946. }
  17947. }
  17948. }
  17949. /**
  17950. * Put everything in place
  17951. *
  17952. * @private
  17953. * @param {number} index
  17954. *
  17955. * @param {boolean} [old]
  17956. * Use old coordinates to prepare an animation into new position
  17957. *
  17958. * @param {number} [opacity]
  17959. */
  17960. render(index, old, opacity) {
  17961. const tick = this, axis = tick.axis, horiz = axis.horiz, pos = tick.pos, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), xy = tick.getPosition(horiz, pos, tickmarkOffset, old), x = xy.x, y = xy.y, reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
  17962. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  17963. const labelOpacity = pick(opacity, tick.label && tick.label.newOpacity, // #15528
  17964. 1);
  17965. opacity = pick(opacity, 1);
  17966. this.isActive = true;
  17967. // Create the grid line
  17968. this.renderGridLine(old, opacity, reverseCrisp);
  17969. // create the tick mark
  17970. this.renderMark(xy, opacity, reverseCrisp);
  17971. // the label is created on init - now move it into place
  17972. this.renderLabel(xy, old, labelOpacity, index);
  17973. tick.isNew = false;
  17974. fireEvent(this, 'afterRender');
  17975. }
  17976. /**
  17977. * Renders the gridLine.
  17978. *
  17979. * @private
  17980. * @function Highcharts.Tick#renderGridLine
  17981. * @param {boolean} old Whether or not the tick is old
  17982. * @param {number} opacity The opacity of the grid line
  17983. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  17984. */
  17985. renderGridLine(old, opacity, reverseCrisp) {
  17986. const tick = this, axis = tick.axis, options = axis.options, attribs = {}, pos = tick.pos, type = tick.type, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), renderer = axis.chart.renderer;
  17987. let gridLine = tick.gridLine, gridLinePath, gridLineWidth = options.gridLineWidth, gridLineColor = options.gridLineColor, dashStyle = options.gridLineDashStyle;
  17988. if (tick.type === 'minor') {
  17989. gridLineWidth = options.minorGridLineWidth;
  17990. gridLineColor = options.minorGridLineColor;
  17991. dashStyle = options.minorGridLineDashStyle;
  17992. }
  17993. if (!gridLine) {
  17994. if (!axis.chart.styledMode) {
  17995. attribs.stroke = gridLineColor;
  17996. attribs['stroke-width'] = gridLineWidth || 0;
  17997. attribs.dashstyle = dashStyle;
  17998. }
  17999. if (!type) {
  18000. attribs.zIndex = 1;
  18001. }
  18002. if (old) {
  18003. opacity = 0;
  18004. }
  18005. /**
  18006. * The rendered grid line of the tick.
  18007. * @name Highcharts.Tick#gridLine
  18008. * @type {Highcharts.SVGElement|undefined}
  18009. */
  18010. tick.gridLine = gridLine = renderer.path()
  18011. .attr(attribs)
  18012. .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
  18013. .add(axis.gridGroup);
  18014. }
  18015. if (gridLine) {
  18016. gridLinePath = axis.getPlotLinePath({
  18017. value: pos + tickmarkOffset,
  18018. lineWidth: gridLine.strokeWidth() * reverseCrisp,
  18019. force: 'pass',
  18020. old: old,
  18021. acrossPanes: false // #18025
  18022. });
  18023. // If the parameter 'old' is set, the current call will be followed
  18024. // by another call, therefore do not do any animations this time
  18025. if (gridLinePath) {
  18026. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  18027. d: gridLinePath,
  18028. opacity: opacity
  18029. });
  18030. }
  18031. }
  18032. }
  18033. /**
  18034. * Renders the tick mark.
  18035. *
  18036. * @private
  18037. * @function Highcharts.Tick#renderMark
  18038. * @param {Highcharts.PositionObject} xy The position vector of the mark
  18039. * @param {number} opacity The opacity of the mark
  18040. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  18041. */
  18042. renderMark(xy, opacity, reverseCrisp) {
  18043. const tick = this, axis = tick.axis, options = axis.options, renderer = axis.chart.renderer, type = tick.type, tickSize = axis.tickSize(type ? type + 'Tick' : 'tick'), x = xy.x, y = xy.y, tickWidth = pick(options[type !== 'minor' ? 'tickWidth' : 'minorTickWidth'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
  18044. tickColor = options[type !== 'minor' ? 'tickColor' : 'minorTickColor'];
  18045. let mark = tick.mark;
  18046. const isNewMark = !mark;
  18047. if (tickSize) {
  18048. // negate the length
  18049. if (axis.opposite) {
  18050. tickSize[0] = -tickSize[0];
  18051. }
  18052. // First time, create it
  18053. if (!mark) {
  18054. /**
  18055. * The rendered mark of the tick.
  18056. * @name Highcharts.Tick#mark
  18057. * @type {Highcharts.SVGElement|undefined}
  18058. */
  18059. tick.mark = mark = renderer.path()
  18060. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  18061. .add(axis.axisGroup);
  18062. if (!axis.chart.styledMode) {
  18063. mark.attr({
  18064. stroke: tickColor,
  18065. 'stroke-width': tickWidth
  18066. });
  18067. }
  18068. }
  18069. mark[isNewMark ? 'attr' : 'animate']({
  18070. d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, axis.horiz, renderer),
  18071. opacity: opacity
  18072. });
  18073. }
  18074. }
  18075. /**
  18076. * Renders the tick label.
  18077. * Note: The label should already be created in init(), so it should only
  18078. * have to be moved into place.
  18079. *
  18080. * @private
  18081. * @function Highcharts.Tick#renderLabel
  18082. * @param {Highcharts.PositionObject} xy The position vector of the label
  18083. * @param {boolean} old Whether or not the tick is old
  18084. * @param {number} opacity The opacity of the label
  18085. * @param {number} index The index of the tick
  18086. */
  18087. renderLabel(xy, old, opacity, index) {
  18088. const tick = this, axis = tick.axis, horiz = axis.horiz, options = axis.options, label = tick.label, labelOptions = options.labels, step = labelOptions.step, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), x = xy.x, y = xy.y;
  18089. let show = true;
  18090. if (label && isNumber(x)) {
  18091. label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
  18092. // Apply show first and show last. If the tick is both first and
  18093. // last, it is a single centered tick, in which case we show the
  18094. // label anyway (#2100).
  18095. if ((tick.isFirst &&
  18096. !tick.isLast &&
  18097. !options.showFirstLabel) ||
  18098. (tick.isLast &&
  18099. !tick.isFirst &&
  18100. !options.showLastLabel)) {
  18101. show = false;
  18102. // Handle label overflow and show or hide accordingly
  18103. }
  18104. else if (horiz &&
  18105. !labelOptions.step &&
  18106. !labelOptions.rotation &&
  18107. !old &&
  18108. opacity !== 0) {
  18109. tick.handleOverflow(xy);
  18110. }
  18111. // apply step
  18112. if (step && index % step) {
  18113. // show those indices dividable by step
  18114. show = false;
  18115. }
  18116. // Set the new position, and show or hide
  18117. if (show && isNumber(xy.y)) {
  18118. xy.opacity = opacity;
  18119. label[tick.isNewLabel ? 'attr' : 'animate'](xy).show(true);
  18120. tick.isNewLabel = false;
  18121. }
  18122. else {
  18123. label.hide(); // #1338, #15863
  18124. tick.isNewLabel = true;
  18125. }
  18126. }
  18127. }
  18128. /**
  18129. * Replace labels with the moved ones to perform animation. Additionally
  18130. * destroy unused labels.
  18131. *
  18132. * @private
  18133. * @function Highcharts.Tick#replaceMovedLabel
  18134. */
  18135. replaceMovedLabel() {
  18136. const tick = this, label = tick.label, axis = tick.axis;
  18137. // Animate and destroy
  18138. if (label && !tick.isNew) {
  18139. label.animate({ opacity: 0 }, void 0, label.destroy);
  18140. delete tick.label;
  18141. }
  18142. axis.isDirty = true;
  18143. tick.label = tick.movedLabel;
  18144. delete tick.movedLabel;
  18145. }
  18146. }
  18147. /* *
  18148. *
  18149. * Default Export
  18150. *
  18151. * */
  18152. /* *
  18153. *
  18154. * API Declarations
  18155. *
  18156. * */
  18157. /**
  18158. * Optional parameters for the tick.
  18159. * @private
  18160. * @interface Highcharts.TickParametersObject
  18161. */ /**
  18162. * Set category for the tick.
  18163. * @name Highcharts.TickParametersObject#category
  18164. * @type {string|undefined}
  18165. */ /**
  18166. * @name Highcharts.TickParametersObject#options
  18167. * @type {Highcharts.Dictionary<any>|undefined}
  18168. */ /**
  18169. * Set tickmarkOffset for the tick.
  18170. * @name Highcharts.TickParametersObject#tickmarkOffset
  18171. * @type {number|undefined}
  18172. */
  18173. /**
  18174. * Additonal time tick information.
  18175. *
  18176. * @interface Highcharts.TimeTicksInfoObject
  18177. * @extends Highcharts.TimeNormalizedObject
  18178. */ /**
  18179. * @name Highcharts.TimeTicksInfoObject#higherRanks
  18180. * @type {Array<string>}
  18181. */ /**
  18182. * @name Highcharts.TimeTicksInfoObject#totalRange
  18183. * @type {number}
  18184. */
  18185. (''); // keeps doclets above in JS file
  18186. return Tick;
  18187. });
  18188. _registerModule(_modules, 'Core/Axis/Axis.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/AxisDefaults.js'], _modules['Core/Color/Color.js'], _modules['Core/Defaults.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (A, AxisDefaults, Color, D, F, H, Tick, U) {
  18189. /* *
  18190. *
  18191. * (c) 2010-2021 Torstein Honsi
  18192. *
  18193. * License: www.highcharts.com/license
  18194. *
  18195. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  18196. *
  18197. * */
  18198. const { animObject } = A;
  18199. const { defaultOptions } = D;
  18200. const { registerEventOptions } = F;
  18201. const { deg2rad } = H;
  18202. const { arrayMax, arrayMin, clamp, correctFloat, defined, destroyObjectProperties, erase, error, extend, fireEvent, getClosestDistance, insertItem, isArray, isNumber, isString, merge, normalizeTickInterval, objectEach, pick, relativeLength, removeEvent, splat, syncTimeout } = U;
  18203. const getNormalizedTickInterval = (axis, tickInterval) => normalizeTickInterval(tickInterval, void 0, void 0, pick(axis.options.allowDecimals,
  18204. // If the tick interval is greather than 0.5, avoid decimals, as
  18205. // linear axes are often used to render discrete values (#3363). If
  18206. // a tick amount is set, allow decimals by default, as it increases
  18207. // the chances for a good fit.
  18208. tickInterval < 0.5 || axis.tickAmount !== void 0), !!axis.tickAmount);
  18209. /* *
  18210. *
  18211. * Class
  18212. *
  18213. * */
  18214. /**
  18215. * Create a new axis object. Called internally when instanciating a new chart or
  18216. * adding axes by {@link Highcharts.Chart#addAxis}.
  18217. *
  18218. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  18219. * series cartesian chart, there is one X axis and one Y axis.
  18220. *
  18221. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  18222. * an array of Axis objects. If there is only one axis, it can be referenced
  18223. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  18224. * pattern goes for Y axes.
  18225. *
  18226. * If you need to get the axes from a series object, use the `series.xAxis` and
  18227. * `series.yAxis` properties. These are not arrays, as one series can only be
  18228. * associated to one X and one Y axis.
  18229. *
  18230. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  18231. * the axis configuration options, and get the axis by
  18232. * {@link Highcharts.Chart#get}.
  18233. *
  18234. * Configuration options for the axes are given in options.xAxis and
  18235. * options.yAxis.
  18236. *
  18237. * @class
  18238. * @name Highcharts.Axis
  18239. *
  18240. * @param {Highcharts.Chart} chart
  18241. * The Chart instance to apply the axis on.
  18242. *
  18243. * @param {Highcharts.AxisOptions} userOptions
  18244. * Axis options
  18245. */
  18246. class Axis {
  18247. /* *
  18248. *
  18249. * Constructors
  18250. *
  18251. * */
  18252. constructor(chart, userOptions, coll) {
  18253. this.alternateBands = void 0;
  18254. this.bottom = void 0;
  18255. this.chart = void 0;
  18256. this.closestPointRange = void 0;
  18257. this.coll = void 0;
  18258. this.eventOptions = void 0;
  18259. this.hasNames = void 0;
  18260. this.hasVisibleSeries = void 0;
  18261. this.height = void 0;
  18262. this.index = void 0;
  18263. this.isLinked = void 0;
  18264. this.labelEdge = void 0; // @todo
  18265. this.labelFormatter = void 0;
  18266. this.left = void 0;
  18267. this.len = void 0;
  18268. this.max = void 0;
  18269. this.maxLabelLength = void 0;
  18270. this.min = void 0;
  18271. this.minorTickInterval = void 0;
  18272. this.minorTicks = void 0;
  18273. this.minPixelPadding = void 0;
  18274. this.names = void 0;
  18275. this.offset = void 0;
  18276. this.options = void 0;
  18277. this.overlap = void 0;
  18278. this.paddedTicks = void 0;
  18279. this.plotLinesAndBands = void 0;
  18280. this.plotLinesAndBandsGroups = void 0;
  18281. this.pointRange = void 0;
  18282. this.pointRangePadding = void 0;
  18283. this.pos = void 0;
  18284. this.positiveValuesOnly = void 0;
  18285. this.right = void 0;
  18286. this.series = void 0;
  18287. this.side = void 0;
  18288. this.tickAmount = void 0;
  18289. this.tickInterval = void 0;
  18290. this.tickmarkOffset = void 0;
  18291. this.tickPositions = void 0;
  18292. this.tickRotCorr = void 0;
  18293. this.ticks = void 0;
  18294. this.top = void 0;
  18295. this.transA = void 0;
  18296. this.transB = void 0;
  18297. this.translationSlope = void 0;
  18298. this.userOptions = void 0;
  18299. this.visible = void 0;
  18300. this.width = void 0;
  18301. this.zoomEnabled = void 0;
  18302. this.init(chart, userOptions, coll);
  18303. }
  18304. /* *
  18305. *
  18306. * Functions
  18307. *
  18308. * */
  18309. /**
  18310. * Overrideable function to initialize the axis.
  18311. *
  18312. * @see {@link Axis}
  18313. *
  18314. * @function Highcharts.Axis#init
  18315. *
  18316. * @param {Highcharts.Chart} chart
  18317. * The Chart instance to apply the axis on.
  18318. *
  18319. * @param {AxisOptions} userOptions
  18320. * Axis options.
  18321. *
  18322. * @emits Highcharts.Axis#event:afterInit
  18323. * @emits Highcharts.Axis#event:init
  18324. */
  18325. init(chart, userOptions, coll = this.coll) {
  18326. const isXAxis = coll === 'xAxis', axis = this;
  18327. /**
  18328. * The Chart that the axis belongs to.
  18329. *
  18330. * @name Highcharts.Axis#chart
  18331. * @type {Highcharts.Chart}
  18332. */
  18333. axis.chart = chart;
  18334. /**
  18335. * Whether the axis is horizontal.
  18336. *
  18337. * @name Highcharts.Axis#horiz
  18338. * @type {boolean|undefined}
  18339. */
  18340. axis.horiz = axis.isZAxis || (chart.inverted ? !isXAxis : isXAxis);
  18341. /**
  18342. * Whether the axis is the x-axis.
  18343. *
  18344. * @name Highcharts.Axis#isXAxis
  18345. * @type {boolean|undefined}
  18346. */
  18347. axis.isXAxis = isXAxis;
  18348. /**
  18349. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  18350. * or `colorAxis`. Corresponds to properties on Chart, for example
  18351. * {@link Chart.xAxis}.
  18352. *
  18353. * @name Highcharts.Axis#coll
  18354. * @type {string}
  18355. */
  18356. axis.coll = coll;
  18357. fireEvent(this, 'init', { userOptions: userOptions });
  18358. // Needed in setOptions
  18359. axis.opposite = pick(userOptions.opposite, axis.opposite);
  18360. /**
  18361. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  18362. * is bottom and 3 is left.
  18363. *
  18364. * @name Highcharts.Axis#side
  18365. * @type {number}
  18366. */
  18367. axis.side = pick(userOptions.side, axis.side, (axis.horiz ?
  18368. (axis.opposite ? 0 : 2) : // top : bottom
  18369. (axis.opposite ? 1 : 3)) // right : left
  18370. );
  18371. /**
  18372. * Current options for the axis after merge of defaults and user's
  18373. * options.
  18374. *
  18375. * @name Highcharts.Axis#options
  18376. * @type {Highcharts.AxisOptions}
  18377. */
  18378. axis.setOptions(userOptions);
  18379. const options = this.options, labelsOptions = options.labels, type = options.type;
  18380. /**
  18381. * User's options for this axis without defaults.
  18382. *
  18383. * @name Highcharts.Axis#userOptions
  18384. * @type {Highcharts.AxisOptions}
  18385. */
  18386. axis.userOptions = userOptions;
  18387. axis.minPixelPadding = 0;
  18388. /**
  18389. * Whether the axis is reversed. Based on the `axis.reversed`,
  18390. * option, but inverted charts have reversed xAxis by default.
  18391. *
  18392. * @name Highcharts.Axis#reversed
  18393. * @type {boolean}
  18394. */
  18395. axis.reversed = pick(options.reversed, axis.reversed);
  18396. axis.visible = options.visible;
  18397. axis.zoomEnabled = options.zoomEnabled;
  18398. // Initial categories
  18399. axis.hasNames =
  18400. type === 'category' || options.categories === true;
  18401. /**
  18402. * If categories are present for the axis, names are used instead of
  18403. * numbers for that axis.
  18404. *
  18405. * Since Highcharts 3.0, categories can also be extracted by giving each
  18406. * point a name and setting axis type to `category`. However, if you
  18407. * have multiple series, best practice remains defining the `categories`
  18408. * array.
  18409. *
  18410. * @see [xAxis.categories](/highcharts/xAxis.categories)
  18411. *
  18412. * @name Highcharts.Axis#categories
  18413. * @type {Array<string>}
  18414. * @readonly
  18415. */
  18416. axis.categories = options.categories || (axis.hasNames ? [] : void 0);
  18417. if (!axis.names) { // Preserve on update (#3830)
  18418. axis.names = [];
  18419. axis.names.keys = {};
  18420. }
  18421. // Placeholder for plotlines and plotbands groups
  18422. axis.plotLinesAndBandsGroups = {};
  18423. // Shorthand types
  18424. axis.positiveValuesOnly = !!axis.logarithmic;
  18425. // Flag, if axis is linked to another axis
  18426. axis.isLinked = defined(options.linkedTo);
  18427. /**
  18428. * List of major ticks mapped by postition on axis.
  18429. *
  18430. * @see {@link Highcharts.Tick}
  18431. *
  18432. * @name Highcharts.Axis#ticks
  18433. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  18434. */
  18435. axis.ticks = {};
  18436. axis.labelEdge = [];
  18437. /**
  18438. * List of minor ticks mapped by position on the axis.
  18439. *
  18440. * @see {@link Highcharts.Tick}
  18441. *
  18442. * @name Highcharts.Axis#minorTicks
  18443. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  18444. */
  18445. axis.minorTicks = {};
  18446. // List of plotLines/Bands
  18447. axis.plotLinesAndBands = [];
  18448. // Alternate bands
  18449. axis.alternateBands = {};
  18450. // Axis metrics
  18451. axis.len = 0;
  18452. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  18453. axis.range = options.range;
  18454. axis.offset = options.offset || 0;
  18455. /**
  18456. * The maximum value of the axis. In a logarithmic axis, this is the
  18457. * logarithm of the real value, and the real value can be obtained from
  18458. * {@link Axis#getExtremes}.
  18459. *
  18460. * @name Highcharts.Axis#max
  18461. * @type {number|null}
  18462. */
  18463. axis.max = null;
  18464. /**
  18465. * The minimum value of the axis. In a logarithmic axis, this is the
  18466. * logarithm of the real value, and the real value can be obtained from
  18467. * {@link Axis#getExtremes}.
  18468. *
  18469. * @name Highcharts.Axis#min
  18470. * @type {number|null}
  18471. */
  18472. axis.min = null;
  18473. /**
  18474. * The processed crosshair options.
  18475. *
  18476. * @name Highcharts.Axis#crosshair
  18477. * @type {boolean|Highcharts.AxisCrosshairOptions}
  18478. */
  18479. const crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1]);
  18480. axis.crosshair = crosshair === true ? {} : crosshair;
  18481. // Register. Don't add it again on Axis.update().
  18482. if (chart.axes.indexOf(axis) === -1) { //
  18483. if (isXAxis) { // #2713
  18484. chart.axes.splice(chart.xAxis.length, 0, axis);
  18485. }
  18486. else {
  18487. chart.axes.push(axis);
  18488. }
  18489. insertItem(this, chart[this.coll]);
  18490. }
  18491. chart.orderItems(axis.coll);
  18492. /**
  18493. * All series associated to the axis.
  18494. *
  18495. * @name Highcharts.Axis#series
  18496. * @type {Array<Highcharts.Series>}
  18497. */
  18498. axis.series = axis.series || []; // populated by Series
  18499. // Reversed axis
  18500. if (chart.inverted &&
  18501. !axis.isZAxis &&
  18502. isXAxis &&
  18503. typeof axis.reversed === 'undefined') {
  18504. axis.reversed = true;
  18505. }
  18506. axis.labelRotation = isNumber(labelsOptions.rotation) ?
  18507. labelsOptions.rotation :
  18508. void 0;
  18509. // Register event listeners
  18510. registerEventOptions(axis, options);
  18511. fireEvent(this, 'afterInit');
  18512. }
  18513. /**
  18514. * Merge and set options.
  18515. *
  18516. * @private
  18517. * @function Highcharts.Axis#setOptions
  18518. *
  18519. * @param {Highcharts.AxisOptions} userOptions
  18520. * Axis options.
  18521. *
  18522. * @emits Highcharts.Axis#event:afterSetOptions
  18523. */
  18524. setOptions(userOptions) {
  18525. this.options = merge(AxisDefaults.defaultXAxisOptions, (this.coll === 'yAxis') && AxisDefaults.defaultYAxisOptions, [
  18526. AxisDefaults.defaultTopAxisOptions,
  18527. AxisDefaults.defaultRightAxisOptions,
  18528. AxisDefaults.defaultBottomAxisOptions,
  18529. AxisDefaults.defaultLeftAxisOptions
  18530. ][this.side], merge(
  18531. // if set in setOptions (#1053):
  18532. defaultOptions[this.coll], userOptions));
  18533. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  18534. }
  18535. /**
  18536. * The default label formatter. The context is a special config object for
  18537. * the label. In apps, use the
  18538. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  18539. * instead, except when a modification is needed.
  18540. *
  18541. * @function Highcharts.Axis#defaultLabelFormatter
  18542. *
  18543. * @param {Highcharts.AxisLabelsFormatterContextObject} this
  18544. * Formatter context of axis label.
  18545. *
  18546. * @param {Highcharts.AxisLabelsFormatterContextObject} [ctx]
  18547. * Formatter context of axis label.
  18548. *
  18549. * @return {string}
  18550. * The formatted label content.
  18551. */
  18552. defaultLabelFormatter(ctx) {
  18553. const axis = this.axis, chart = this.chart, { numberFormatter } = chart, value = isNumber(this.value) ? this.value : NaN, time = axis.chart.time, categories = axis.categories, dateTimeLabelFormat = this.dateTimeLabelFormat, lang = defaultOptions.lang, numericSymbols = lang.numericSymbols, numSymMagnitude = lang.numericSymbolMagnitude || 1000,
  18554. // make sure the same symbol is added for all labels on a linear
  18555. // axis
  18556. numericSymbolDetector = axis.logarithmic ?
  18557. Math.abs(value) :
  18558. axis.tickInterval;
  18559. let i = numericSymbols && numericSymbols.length, multi, ret;
  18560. if (categories) {
  18561. ret = `${this.value}`;
  18562. }
  18563. else if (dateTimeLabelFormat) { // datetime axis
  18564. ret = time.dateFormat(dateTimeLabelFormat, value);
  18565. }
  18566. else if (i && numericSymbolDetector >= 1000) {
  18567. // Decide whether we should add a numeric symbol like k (thousands)
  18568. // or M (millions). If we are to enable this in tooltip or other
  18569. // places as well, we can move this logic to the numberFormatter and
  18570. // enable it by a parameter.
  18571. while (i-- && typeof ret === 'undefined') {
  18572. multi = Math.pow(numSymMagnitude, i + 1);
  18573. if (
  18574. // Only accept a numeric symbol when the distance is more
  18575. // than a full unit. So for example if the symbol is k, we
  18576. // don't accept numbers like 0.5k.
  18577. numericSymbolDetector >= multi &&
  18578. // Accept one decimal before the symbol. Accepts 0.5k but
  18579. // not 0.25k. How does this work with the previous?
  18580. (value * 10) % multi === 0 &&
  18581. numericSymbols[i] !== null &&
  18582. value !== 0) { // #5480
  18583. ret = numberFormatter(value / multi, -1) + numericSymbols[i];
  18584. }
  18585. }
  18586. }
  18587. if (typeof ret === 'undefined') {
  18588. if (Math.abs(value) >= 10000) { // add thousands separators
  18589. ret = numberFormatter(value, -1);
  18590. }
  18591. else { // small numbers
  18592. ret = numberFormatter(value, -1, void 0, ''); // #2466
  18593. }
  18594. }
  18595. return ret;
  18596. }
  18597. /**
  18598. * Get the minimum and maximum for the series of each axis. The function
  18599. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  18600. *
  18601. * @private
  18602. * @function Highcharts.Axis#getSeriesExtremes
  18603. *
  18604. * @emits Highcharts.Axis#event:afterGetSeriesExtremes
  18605. * @emits Highcharts.Axis#event:getSeriesExtremes
  18606. */
  18607. getSeriesExtremes() {
  18608. const axis = this, chart = axis.chart;
  18609. let xExtremes;
  18610. fireEvent(this, 'getSeriesExtremes', null, function () {
  18611. axis.hasVisibleSeries = false;
  18612. // Reset properties in case we're redrawing (#3353)
  18613. axis.dataMin = axis.dataMax = axis.threshold = null;
  18614. axis.softThreshold = !axis.isXAxis;
  18615. // Loop through this axis' series
  18616. axis.series.forEach(function (series) {
  18617. if (series.visible ||
  18618. !chart.options.chart.ignoreHiddenSeries) {
  18619. const seriesOptions = series.options;
  18620. let xData, threshold = seriesOptions.threshold, seriesDataMin, seriesDataMax;
  18621. axis.hasVisibleSeries = true;
  18622. // Validate threshold in logarithmic axes
  18623. if (axis.positiveValuesOnly && threshold <= 0) {
  18624. threshold = null;
  18625. }
  18626. // Get dataMin and dataMax for X axes
  18627. if (axis.isXAxis) {
  18628. xData = series.xData;
  18629. if (xData && xData.length) {
  18630. xData = axis.logarithmic ?
  18631. xData.filter((x) => x > 0) :
  18632. xData;
  18633. xExtremes = series.getXExtremes(xData);
  18634. // If xData contains values which is not numbers,
  18635. // then filter them out. To prevent performance hit,
  18636. // we only do this after we have already found
  18637. // seriesDataMin because in most cases all data is
  18638. // valid. #5234.
  18639. seriesDataMin = xExtremes.min;
  18640. seriesDataMax = xExtremes.max;
  18641. if (!isNumber(seriesDataMin) &&
  18642. // #5010:
  18643. !(seriesDataMin instanceof Date)) {
  18644. xData = xData.filter(isNumber);
  18645. xExtremes = series.getXExtremes(xData);
  18646. // Do it again with valid data
  18647. seriesDataMin = xExtremes.min;
  18648. seriesDataMax = xExtremes.max;
  18649. }
  18650. if (xData.length) {
  18651. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  18652. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  18653. }
  18654. }
  18655. // Get dataMin and dataMax for Y axes, as well as handle
  18656. // stacking and processed data
  18657. }
  18658. else {
  18659. // Get this particular series extremes
  18660. const dataExtremes = series.applyExtremes();
  18661. // Get the dataMin and dataMax so far. If percentage is
  18662. // used, the min and max are always 0 and 100. If
  18663. // seriesDataMin and seriesDataMax is null, then series
  18664. // doesn't have active y data, we continue with nulls
  18665. if (isNumber(dataExtremes.dataMin)) {
  18666. seriesDataMin = dataExtremes.dataMin;
  18667. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  18668. }
  18669. if (isNumber(dataExtremes.dataMax)) {
  18670. seriesDataMax = dataExtremes.dataMax;
  18671. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  18672. }
  18673. // Adjust to threshold
  18674. if (defined(threshold)) {
  18675. axis.threshold = threshold;
  18676. }
  18677. // If any series has a hard threshold, it takes
  18678. // precedence
  18679. if (!seriesOptions.softThreshold ||
  18680. axis.positiveValuesOnly) {
  18681. axis.softThreshold = false;
  18682. }
  18683. }
  18684. }
  18685. });
  18686. });
  18687. fireEvent(this, 'afterGetSeriesExtremes');
  18688. }
  18689. /**
  18690. * Translate from axis value to pixel position on the chart, or back. Use
  18691. * the `toPixels` and `toValue` functions in applications.
  18692. *
  18693. * @private
  18694. * @function Highcharts.Axis#translate
  18695. */
  18696. translate(val, backwards, cvsCoord, old, handleLog, pointPlacement) {
  18697. const axis = (this.linkedParent || this), // #1417
  18698. localMin = (old && axis.old ? axis.old.min : axis.min);
  18699. if (!isNumber(localMin)) {
  18700. return NaN;
  18701. }
  18702. const minPixelPadding = axis.minPixelPadding, doPostTranslate = (axis.isOrdinal ||
  18703. axis.brokenAxis && axis.brokenAxis.hasBreaks ||
  18704. (axis.logarithmic && handleLog)) && axis.lin2val;
  18705. let sign = 1, cvsOffset = 0, localA = old && axis.old ? axis.old.transA : axis.transA, returnValue = 0;
  18706. if (!localA) {
  18707. localA = axis.transA;
  18708. }
  18709. // In vertical axes, the canvas coordinates start from 0 at the top like
  18710. // in SVG.
  18711. if (cvsCoord) {
  18712. sign *= -1; // canvas coordinates inverts the value
  18713. cvsOffset = axis.len;
  18714. }
  18715. // Handle reversed axis
  18716. if (axis.reversed) {
  18717. sign *= -1;
  18718. cvsOffset -= sign * (axis.sector || axis.len);
  18719. }
  18720. // From pixels to value
  18721. if (backwards) { // reverse translation
  18722. val = val * sign + cvsOffset;
  18723. val -= minPixelPadding;
  18724. // from chart pixel to value:
  18725. returnValue = val / localA + localMin;
  18726. if (doPostTranslate) { // log, ordinal and broken axis
  18727. returnValue = axis.lin2val(returnValue);
  18728. }
  18729. // From value to pixels
  18730. }
  18731. else {
  18732. if (doPostTranslate) { // log, ordinal and broken axis
  18733. val = axis.val2lin(val);
  18734. }
  18735. const value = sign * (val - localMin) * localA;
  18736. returnValue = (!axis.isRadial ? correctFloat(value) : value) +
  18737. cvsOffset +
  18738. (sign * minPixelPadding) +
  18739. (isNumber(pointPlacement) ? localA * pointPlacement : 0);
  18740. }
  18741. return returnValue;
  18742. }
  18743. /**
  18744. * Translate a value in terms of axis units into pixels within the chart.
  18745. *
  18746. * @function Highcharts.Axis#toPixels
  18747. *
  18748. * @param {number} value
  18749. * A value in terms of axis units.
  18750. *
  18751. * @param {boolean} paneCoordinates
  18752. * Whether to return the pixel coordinate relative to the chart or just the
  18753. * axis/pane itself.
  18754. *
  18755. * @return {number}
  18756. * Pixel position of the value on the chart or axis.
  18757. */
  18758. toPixels(value, paneCoordinates) {
  18759. return this.translate(value, false, !this.horiz, void 0, true) +
  18760. (paneCoordinates ? 0 : this.pos);
  18761. }
  18762. /**
  18763. * Translate a pixel position along the axis to a value in terms of axis
  18764. * units.
  18765. *
  18766. * @function Highcharts.Axis#toValue
  18767. *
  18768. * @param {number} pixel
  18769. * The pixel value coordinate.
  18770. *
  18771. * @param {boolean} [paneCoordinates=false]
  18772. * Whether the input pixel is relative to the chart or just the axis/pane
  18773. * itself.
  18774. *
  18775. * @return {number}
  18776. * The axis value.
  18777. */
  18778. toValue(pixel, paneCoordinates) {
  18779. return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, void 0, true);
  18780. }
  18781. /**
  18782. * Create the path for a plot line that goes from the given value on
  18783. * this axis, across the plot to the opposite side. Also used internally for
  18784. * grid lines and crosshairs.
  18785. *
  18786. * @function Highcharts.Axis#getPlotLinePath
  18787. *
  18788. * @param {Highcharts.AxisPlotLinePathOptionsObject} options
  18789. * Options for the path.
  18790. *
  18791. * @return {Highcharts.SVGPathArray|null}
  18792. * The SVG path definition for the plot line.
  18793. */
  18794. getPlotLinePath(options) {
  18795. const axis = this, chart = axis.chart, axisLeft = axis.left, axisTop = axis.top, old = options.old, value = options.value, lineWidth = options.lineWidth, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, cWidth = (old && chart.oldChartWidth) || chart.chartWidth, transB = axis.transB;
  18796. let translatedValue = options.translatedValue, force = options.force, x1, y1, x2, y2, skip;
  18797. // eslint-disable-next-line valid-jsdoc
  18798. /**
  18799. * Check if x is between a and b. If not, either move to a/b
  18800. * or skip, depending on the force parameter.
  18801. * @private
  18802. */
  18803. function between(x, a, b) {
  18804. if (force !== 'pass' && (x < a || x > b)) {
  18805. if (force) {
  18806. x = clamp(x, a, b);
  18807. }
  18808. else {
  18809. skip = true;
  18810. }
  18811. }
  18812. return x;
  18813. }
  18814. const evt = {
  18815. value: value,
  18816. lineWidth: lineWidth,
  18817. old: old,
  18818. force: force,
  18819. acrossPanes: options.acrossPanes,
  18820. translatedValue: translatedValue
  18821. };
  18822. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  18823. translatedValue = pick(translatedValue, axis.translate(value, void 0, void 0, old));
  18824. // Keep the translated value within sane bounds, and avoid Infinity
  18825. // to fail the isNumber test (#7709).
  18826. translatedValue = clamp(translatedValue, -1e5, 1e5);
  18827. x1 = x2 = Math.round(translatedValue + transB);
  18828. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  18829. if (!isNumber(translatedValue)) { // no min or max
  18830. skip = true;
  18831. force = false; // #7175, don't force it when path is invalid
  18832. }
  18833. else if (axis.horiz) {
  18834. y1 = axisTop;
  18835. y2 = cHeight - axis.bottom;
  18836. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  18837. }
  18838. else {
  18839. x1 = axisLeft;
  18840. x2 = cWidth - axis.right;
  18841. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  18842. }
  18843. e.path = skip && !force ?
  18844. null :
  18845. chart.renderer.crispLine([['M', x1, y1], ['L', x2, y2]], lineWidth || 1);
  18846. });
  18847. return evt.path;
  18848. }
  18849. /**
  18850. * Internal function to get the tick positions of a linear axis to round
  18851. * values like whole tens or every five.
  18852. *
  18853. * @function Highcharts.Axis#getLinearTickPositions
  18854. *
  18855. * @param {number} tickInterval
  18856. * The normalized tick interval.
  18857. *
  18858. * @param {number} min
  18859. * Axis minimum.
  18860. *
  18861. * @param {number} max
  18862. * Axis maximum.
  18863. *
  18864. * @return {Array<number>}
  18865. * An array of axis values where ticks should be placed.
  18866. */
  18867. getLinearTickPositions(tickInterval, min, max) {
  18868. const roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval), roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval), tickPositions = [];
  18869. let pos, lastPos, precision;
  18870. // When the precision is higher than what we filter out in
  18871. // correctFloat, skip it (#6183).
  18872. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  18873. precision = 20;
  18874. }
  18875. // For single points, add a tick regardless of the relative position
  18876. // (#2662, #6274)
  18877. if (this.single) {
  18878. return [min];
  18879. }
  18880. // Populate the intermediate values
  18881. pos = roundedMin;
  18882. while (pos <= roundedMax) {
  18883. // Place the tick on the rounded value
  18884. tickPositions.push(pos);
  18885. // Always add the raw tickInterval, not the corrected one.
  18886. pos = correctFloat(pos + tickInterval, precision);
  18887. // If the interval is not big enough in the current min - max range
  18888. // to actually increase the loop variable, we need to break out to
  18889. // prevent endless loop. Issue #619
  18890. if (pos === lastPos) {
  18891. break;
  18892. }
  18893. // Record the last value
  18894. lastPos = pos;
  18895. }
  18896. return tickPositions;
  18897. }
  18898. /**
  18899. * Resolve the new minorTicks/minorTickInterval options into the legacy
  18900. * loosely typed minorTickInterval option.
  18901. *
  18902. * @function Highcharts.Axis#getMinorTickInterval
  18903. *
  18904. * @return {number|"auto"|null}
  18905. * Legacy option
  18906. */
  18907. getMinorTickInterval() {
  18908. const options = this.options;
  18909. if (options.minorTicks === true) {
  18910. return pick(options.minorTickInterval, 'auto');
  18911. }
  18912. if (options.minorTicks === false) {
  18913. return null;
  18914. }
  18915. return options.minorTickInterval;
  18916. }
  18917. /**
  18918. * Internal function to return the minor tick positions. For logarithmic
  18919. * axes, the same logic as for major ticks is reused.
  18920. *
  18921. * @function Highcharts.Axis#getMinorTickPositions
  18922. *
  18923. * @return {Array<number>}
  18924. * An array of axis values where ticks should be placed.
  18925. */
  18926. getMinorTickPositions() {
  18927. const axis = this, options = axis.options, tickPositions = axis.tickPositions, minorTickInterval = axis.minorTickInterval, pointRangePadding = axis.pointRangePadding || 0, min = axis.min - pointRangePadding, // #1498
  18928. max = axis.max + pointRangePadding, // #1498
  18929. range = max - min;
  18930. let minorTickPositions = [], pos;
  18931. // If minor ticks get too dense, they are hard to read, and may cause
  18932. // long running script. So we don't draw them.
  18933. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  18934. const logarithmic = axis.logarithmic;
  18935. if (logarithmic) {
  18936. // For each interval in the major ticks, compute the minor ticks
  18937. // separately.
  18938. this.paddedTicks.forEach(function (_pos, i, paddedTicks) {
  18939. if (i) {
  18940. minorTickPositions.push.apply(minorTickPositions, logarithmic.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
  18941. }
  18942. });
  18943. }
  18944. else if (axis.dateTime &&
  18945. this.getMinorTickInterval() === 'auto') { // #1314
  18946. minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
  18947. }
  18948. else {
  18949. for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
  18950. // Very, very, tight grid lines (#5771)
  18951. if (pos === minorTickPositions[0]) {
  18952. break;
  18953. }
  18954. minorTickPositions.push(pos);
  18955. }
  18956. }
  18957. }
  18958. if (minorTickPositions.length !== 0) {
  18959. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  18960. }
  18961. return minorTickPositions;
  18962. }
  18963. /**
  18964. * Adjust the min and max for the minimum range. Keep in mind that the
  18965. * series data is not yet processed, so we don't have information on data
  18966. * cropping and grouping, or updated `axis.pointRange` or
  18967. * `series.pointRange`. The data can't be processed until we have finally
  18968. * established min and max.
  18969. *
  18970. * @private
  18971. * @function Highcharts.Axis#adjustForMinRange
  18972. */
  18973. adjustForMinRange() {
  18974. const axis = this, options = axis.options, logarithmic = axis.logarithmic;
  18975. let min = axis.min, max = axis.max, zoomOffset, spaceAvailable, closestDataRange, minArgs, maxArgs, minRange;
  18976. // Set the automatic minimum range based on the closest point distance
  18977. if (axis.isXAxis &&
  18978. typeof axis.minRange === 'undefined' &&
  18979. !logarithmic) {
  18980. if (defined(options.min) ||
  18981. defined(options.max) ||
  18982. defined(options.floor) ||
  18983. defined(options.ceiling)) {
  18984. axis.minRange = null; // don't do this again
  18985. }
  18986. else {
  18987. // Find the closest distance between raw data points, as opposed
  18988. // to closestPointRange that applies to processed points
  18989. // (cropped and grouped)
  18990. closestDataRange = getClosestDistance(axis.series.map((s) => { var _a;
  18991. // If xIncrement, we only need to measure the two first
  18992. // points to get the distance. Saves processing time.
  18993. return (s.xIncrement ? (_a = s.xData) === null || _a === void 0 ? void 0 : _a.slice(0, 2) : s.xData) || []; })) || 0;
  18994. axis.minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
  18995. }
  18996. }
  18997. // if minRange is exceeded, adjust
  18998. if (max - min < axis.minRange) {
  18999. spaceAvailable =
  19000. axis.dataMax - axis.dataMin >=
  19001. axis.minRange;
  19002. minRange = axis.minRange;
  19003. zoomOffset = (minRange - max + min) / 2;
  19004. // if min and max options have been set, don't go beyond it
  19005. minArgs = [
  19006. min - zoomOffset,
  19007. pick(options.min, min - zoomOffset)
  19008. ];
  19009. // If space is available, stay within the data range
  19010. if (spaceAvailable) {
  19011. minArgs[2] = logarithmic ?
  19012. logarithmic.log2lin(axis.dataMin) :
  19013. axis.dataMin;
  19014. }
  19015. min = arrayMax(minArgs);
  19016. maxArgs = [
  19017. min + minRange,
  19018. pick(options.max, min + minRange)
  19019. ];
  19020. // If space is availabe, stay within the data range
  19021. if (spaceAvailable) {
  19022. maxArgs[2] = logarithmic ?
  19023. logarithmic.log2lin(axis.dataMax) :
  19024. axis.dataMax;
  19025. }
  19026. max = arrayMin(maxArgs);
  19027. // now if the max is adjusted, adjust the min back
  19028. if (max - min < minRange) {
  19029. minArgs[0] = max - minRange;
  19030. minArgs[1] = pick(options.min, max - minRange);
  19031. min = arrayMax(minArgs);
  19032. }
  19033. }
  19034. // Record modified extremes
  19035. axis.min = min;
  19036. axis.max = max;
  19037. }
  19038. /**
  19039. * Find the closestPointRange across all series, including the single data
  19040. * series.
  19041. *
  19042. * @private
  19043. * @function Highcharts.Axis#getClosest
  19044. */
  19045. getClosest() {
  19046. let closestSingleDistance, closestDistance;
  19047. if (this.categories) {
  19048. closestDistance = 1;
  19049. }
  19050. else {
  19051. const singleXs = [];
  19052. this.series.forEach(function (series) {
  19053. var _a;
  19054. const seriesClosest = series.closestPointRange, visible = series.visible ||
  19055. !series.chart.options.chart.ignoreHiddenSeries;
  19056. if (((_a = series.xData) === null || _a === void 0 ? void 0 : _a.length) === 1) {
  19057. singleXs.push(series.xData[0]);
  19058. }
  19059. else if (!series.noSharedTooltip &&
  19060. defined(seriesClosest) &&
  19061. visible) {
  19062. closestDistance = defined(closestDistance) ?
  19063. Math.min(closestDistance, seriesClosest) :
  19064. seriesClosest;
  19065. }
  19066. });
  19067. if (singleXs.length) {
  19068. singleXs.sort((a, b) => a - b);
  19069. closestSingleDistance = getClosestDistance([singleXs]);
  19070. }
  19071. }
  19072. if (closestSingleDistance && closestDistance) {
  19073. return Math.min(closestSingleDistance, closestDistance);
  19074. }
  19075. return closestSingleDistance || closestDistance;
  19076. }
  19077. /**
  19078. * When a point name is given and no x, search for the name in the existing
  19079. * categories, or if categories aren't provided, search names or create a
  19080. * new category (#2522).
  19081. *
  19082. * @private
  19083. * @function Highcharts.Axis#nameToX
  19084. *
  19085. * @param {Highcharts.Point} point
  19086. * The point to inspect.
  19087. *
  19088. * @return {number}
  19089. * The X value that the point is given.
  19090. */
  19091. nameToX(point) {
  19092. const explicitCategories = isArray(this.options.categories), names = explicitCategories ? this.categories : this.names;
  19093. let nameX = point.options.x, x;
  19094. point.series.requireSorting = false;
  19095. if (!defined(nameX)) {
  19096. nameX = this.options.uniqueNames && names ?
  19097. (explicitCategories ?
  19098. names.indexOf(point.name) :
  19099. pick(names.keys[point.name], -1)) :
  19100. point.series.autoIncrement();
  19101. }
  19102. if (nameX === -1) { // Not found in currenct categories
  19103. if (!explicitCategories && names) {
  19104. x = names.length;
  19105. }
  19106. }
  19107. else {
  19108. x = nameX;
  19109. }
  19110. // Write the last point's name to the names array
  19111. if (typeof x !== 'undefined') {
  19112. this.names[x] = point.name;
  19113. // Backwards mapping is much faster than array searching (#7725)
  19114. this.names.keys[point.name] = x;
  19115. }
  19116. else if (point.x) {
  19117. x = point.x; // #17438
  19118. }
  19119. return x;
  19120. }
  19121. /**
  19122. * When changes have been done to series data, update the axis.names.
  19123. *
  19124. * @private
  19125. * @function Highcharts.Axis#updateNames
  19126. */
  19127. updateNames() {
  19128. const axis = this, names = this.names, i = names.length;
  19129. if (i > 0) {
  19130. Object.keys(names.keys).forEach(function (key) {
  19131. delete (names.keys)[key];
  19132. });
  19133. names.length = 0;
  19134. this.minRange = this.userMinRange; // Reset
  19135. (this.series || []).forEach(function (series) {
  19136. // Reset incrementer (#5928)
  19137. series.xIncrement = null;
  19138. // When adding a series, points are not yet generated
  19139. if (!series.points || series.isDirtyData) {
  19140. // When we're updating the series with data that is longer
  19141. // than it was, and cropThreshold is passed, we need to make
  19142. // sure that the axis.max is increased _before_ running the
  19143. // premature processData. Otherwise this early iteration of
  19144. // processData will crop the points to axis.max, and the
  19145. // names array will be too short (#5857).
  19146. axis.max = Math.max(axis.max, series.xData.length - 1);
  19147. series.processData();
  19148. series.generatePoints();
  19149. }
  19150. series.data.forEach(function (point, i) {
  19151. let x;
  19152. if (point &&
  19153. point.options &&
  19154. typeof point.name !== 'undefined' // #9562
  19155. ) {
  19156. x = axis.nameToX(point);
  19157. if (typeof x !== 'undefined' && x !== point.x) {
  19158. point.x = x;
  19159. series.xData[i] = x;
  19160. }
  19161. }
  19162. });
  19163. });
  19164. }
  19165. }
  19166. /**
  19167. * Update translation information.
  19168. *
  19169. * @private
  19170. * @function Highcharts.Axis#setAxisTranslation
  19171. *
  19172. * @emits Highcharts.Axis#event:afterSetAxisTranslation
  19173. */
  19174. setAxisTranslation() {
  19175. const axis = this, range = axis.max - axis.min, linkedParent = axis.linkedParent, hasCategories = !!axis.categories, isXAxis = axis.isXAxis;
  19176. let pointRange = axis.axisPointRange || 0, closestPointRange, minPointOffset = 0, pointRangePadding = 0, ordinalCorrection, transA = axis.transA;
  19177. // Adjust translation for padding. Y axis with categories need to go
  19178. // through the same (#1784).
  19179. if (isXAxis || hasCategories || pointRange) {
  19180. // Get the closest points
  19181. closestPointRange = axis.getClosest();
  19182. if (linkedParent) {
  19183. minPointOffset = linkedParent.minPointOffset;
  19184. pointRangePadding = linkedParent.pointRangePadding;
  19185. }
  19186. else {
  19187. axis.series.forEach(function (series) {
  19188. const seriesPointRange = hasCategories ?
  19189. 1 :
  19190. (isXAxis ?
  19191. pick(series.options.pointRange, closestPointRange, 0) :
  19192. (axis.axisPointRange || 0)), // #2806
  19193. pointPlacement = series.options.pointPlacement;
  19194. pointRange = Math.max(pointRange, seriesPointRange);
  19195. if (!axis.single || hasCategories) {
  19196. // TODO: series should internally set x- and y-
  19197. // pointPlacement to simplify this logic.
  19198. const isPointPlacementAxis = series.is('xrange') ?
  19199. !isXAxis :
  19200. isXAxis;
  19201. // minPointOffset is the value padding to the left of
  19202. // the axis in order to make room for points with a
  19203. // pointRange, typically columns. When the
  19204. // pointPlacement option is 'between' or 'on', this
  19205. // padding does not apply.
  19206. minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && isString(pointPlacement) ?
  19207. 0 :
  19208. seriesPointRange / 2);
  19209. // Determine the total padding needed to the length of
  19210. // the axis to make room for the pointRange. If the
  19211. // series' pointPlacement is 'on', no padding is added.
  19212. pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
  19213. 0 :
  19214. seriesPointRange);
  19215. }
  19216. });
  19217. }
  19218. // Record minPointOffset and pointRangePadding
  19219. ordinalCorrection = (axis.ordinal && axis.ordinal.slope && closestPointRange) ?
  19220. axis.ordinal.slope / closestPointRange :
  19221. 1; // #988, #1853
  19222. axis.minPointOffset = minPointOffset =
  19223. minPointOffset * ordinalCorrection;
  19224. axis.pointRangePadding =
  19225. pointRangePadding = pointRangePadding * ordinalCorrection;
  19226. // pointRange means the width reserved for each point, like in a
  19227. // column chart
  19228. axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
  19229. // closestPointRange means the closest distance between points. In
  19230. // columns it is mostly equal to pointRange, but in lines pointRange
  19231. // is 0 while closestPointRange is some other value
  19232. if (isXAxis && closestPointRange) {
  19233. axis.closestPointRange = closestPointRange;
  19234. }
  19235. }
  19236. // Secondary values
  19237. axis.translationSlope = axis.transA = transA =
  19238. axis.staticScale ||
  19239. axis.len / ((range + pointRangePadding) || 1);
  19240. // Translation addend
  19241. axis.transB = axis.horiz ? axis.left : axis.bottom;
  19242. axis.minPixelPadding = transA * minPointOffset;
  19243. fireEvent(this, 'afterSetAxisTranslation');
  19244. }
  19245. /**
  19246. * @private
  19247. * @function Highcharts.Axis#minFromRange
  19248. */
  19249. minFromRange() {
  19250. const axis = this;
  19251. return axis.max - axis.range;
  19252. }
  19253. /**
  19254. * Set the tick positions to round values and optionally extend the extremes
  19255. * to the nearest tick.
  19256. *
  19257. * @private
  19258. * @function Highcharts.Axis#setTickInterval
  19259. *
  19260. * @param {boolean} secondPass
  19261. * TO-DO: parameter description
  19262. *
  19263. * @emits Highcharts.Axis#event:foundExtremes
  19264. */
  19265. setTickInterval(secondPass) {
  19266. const axis = this, chart = axis.chart, log = axis.logarithmic, options = axis.options, isXAxis = axis.isXAxis, isLinked = axis.isLinked, tickPixelIntervalOption = options.tickPixelInterval, categories = axis.categories, softThreshold = axis.softThreshold;
  19267. let maxPadding = options.maxPadding, minPadding = options.minPadding, length, linkedParentExtremes,
  19268. // Only non-negative tickInterval is valid, #12961
  19269. tickIntervalOption = isNumber(options.tickInterval) && options.tickInterval >= 0 ?
  19270. options.tickInterval : void 0, threshold = isNumber(axis.threshold) ? axis.threshold : null, thresholdMin, thresholdMax, hardMin, hardMax;
  19271. if (!axis.dateTime && !categories && !isLinked) {
  19272. this.getTickAmount();
  19273. }
  19274. // Min or max set either by zooming/setExtremes or initial options
  19275. hardMin = pick(axis.userMin, options.min);
  19276. hardMax = pick(axis.userMax, options.max);
  19277. // Linked axis gets the extremes from the parent axis
  19278. if (isLinked) {
  19279. axis.linkedParent = chart[axis.coll][options.linkedTo];
  19280. linkedParentExtremes = axis.linkedParent.getExtremes();
  19281. axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
  19282. axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
  19283. if (options.type !== axis.linkedParent.options.type) {
  19284. // Can't link axes of different type
  19285. error(11, 1, chart);
  19286. }
  19287. // Initial min and max from the extreme data values
  19288. }
  19289. else {
  19290. // Adjust to hard threshold
  19291. if (softThreshold && defined(threshold)) {
  19292. if (axis.dataMin >= threshold) {
  19293. thresholdMin = threshold;
  19294. minPadding = 0;
  19295. }
  19296. else if (axis.dataMax <= threshold) {
  19297. thresholdMax = threshold;
  19298. maxPadding = 0;
  19299. }
  19300. }
  19301. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  19302. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  19303. }
  19304. if (log) {
  19305. if (axis.positiveValuesOnly &&
  19306. !secondPass &&
  19307. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
  19308. // Can't plot negative values on log axis
  19309. error(10, 1, chart);
  19310. }
  19311. // The correctFloat cures #934, float errors on full tens. But it
  19312. // was too aggressive for #4360 because of conversion back to lin,
  19313. // therefore use precision 15.
  19314. axis.min = correctFloat(log.log2lin(axis.min), 16);
  19315. axis.max = correctFloat(log.log2lin(axis.max), 16);
  19316. }
  19317. // handle zoomed range
  19318. if (axis.range && defined(axis.max)) {
  19319. // #618, #6773:
  19320. axis.userMin = axis.min = hardMin =
  19321. Math.max(axis.dataMin, axis.minFromRange());
  19322. axis.userMax = hardMax = axis.max;
  19323. axis.range = null; // don't use it when running setExtremes
  19324. }
  19325. // Hook for Highcharts Stock Scroller.
  19326. // Consider combining with beforePadding.
  19327. fireEvent(axis, 'foundExtremes');
  19328. // Hook for adjusting this.min and this.max. Used by bubble series.
  19329. if (axis.beforePadding) {
  19330. axis.beforePadding();
  19331. }
  19332. // Adjust min and max for the minimum range
  19333. axis.adjustForMinRange();
  19334. // Handle options for floor, ceiling, softMin and softMax (#6359)
  19335. if (!isNumber(axis.userMin)) {
  19336. if (isNumber(options.softMin) && options.softMin < axis.min) {
  19337. axis.min = hardMin = options.softMin; // #6894
  19338. }
  19339. }
  19340. if (!isNumber(axis.userMax)) {
  19341. if (isNumber(options.softMax) && options.softMax > axis.max) {
  19342. axis.max = hardMax = options.softMax; // #6894
  19343. }
  19344. }
  19345. // Pad the values to get clear of the chart's edges. To avoid
  19346. // tickInterval taking the padding into account, we do this after
  19347. // computing tick interval (#1337).
  19348. if (!categories &&
  19349. !axis.axisPointRange &&
  19350. !(axis.stacking && axis.stacking.usePercentage) &&
  19351. !isLinked &&
  19352. defined(axis.min) &&
  19353. defined(axis.max)) {
  19354. length = axis.max - axis.min;
  19355. if (length) {
  19356. if (!defined(hardMin) && minPadding) {
  19357. axis.min -= length * minPadding;
  19358. }
  19359. if (!defined(hardMax) && maxPadding) {
  19360. axis.max += length * maxPadding;
  19361. }
  19362. }
  19363. }
  19364. if (!isNumber(axis.userMin) && isNumber(options.floor)) {
  19365. axis.min = Math.max(axis.min, options.floor);
  19366. }
  19367. if (!isNumber(axis.userMax) && isNumber(options.ceiling)) {
  19368. axis.max = Math.min(axis.max, options.ceiling);
  19369. }
  19370. // When the threshold is soft, adjust the extreme value only if the data
  19371. // extreme and the padded extreme land on either side of the threshold.
  19372. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  19373. // for -1 because of the default minPadding and startOnTick options.
  19374. // This is prevented by the softThreshold option.
  19375. if (softThreshold && defined(axis.dataMin)) {
  19376. threshold = threshold || 0;
  19377. if (!defined(hardMin) &&
  19378. axis.min < threshold &&
  19379. axis.dataMin >= threshold) {
  19380. axis.min = axis.options.minRange ?
  19381. Math.min(threshold, axis.max -
  19382. axis.minRange) :
  19383. threshold;
  19384. }
  19385. else if (!defined(hardMax) &&
  19386. axis.max > threshold &&
  19387. axis.dataMax <= threshold) {
  19388. axis.max = axis.options.minRange ?
  19389. Math.max(threshold, axis.min +
  19390. axis.minRange) :
  19391. threshold;
  19392. }
  19393. }
  19394. // If min is bigger than highest, or if max less than lowest value, the
  19395. // chart should not render points. (#14417)
  19396. if (isNumber(axis.min) &&
  19397. isNumber(axis.max) &&
  19398. !this.chart.polar &&
  19399. (axis.min > axis.max)) {
  19400. if (defined(axis.options.min)) {
  19401. axis.max = axis.min;
  19402. }
  19403. else if (defined(axis.options.max)) {
  19404. axis.min = axis.max;
  19405. }
  19406. }
  19407. // get tickInterval
  19408. if (axis.min === axis.max ||
  19409. typeof axis.min === 'undefined' ||
  19410. typeof axis.max === 'undefined') {
  19411. axis.tickInterval = 1;
  19412. }
  19413. else if (isLinked &&
  19414. axis.linkedParent &&
  19415. !tickIntervalOption &&
  19416. tickPixelIntervalOption ===
  19417. axis.linkedParent.options.tickPixelInterval) {
  19418. axis.tickInterval = tickIntervalOption =
  19419. axis.linkedParent.tickInterval;
  19420. }
  19421. else {
  19422. axis.tickInterval = pick(tickIntervalOption, this.tickAmount ?
  19423. ((axis.max - axis.min) /
  19424. Math.max(this.tickAmount - 1, 1)) :
  19425. void 0,
  19426. // For categoried axis, 1 is default, for linear axis use
  19427. // tickPix
  19428. categories ?
  19429. 1 :
  19430. // don't let it be more than the data range
  19431. (axis.max - axis.min) *
  19432. tickPixelIntervalOption /
  19433. Math.max(axis.len, tickPixelIntervalOption));
  19434. }
  19435. // Now we're finished detecting min and max, crop and group series data.
  19436. // This is in turn needed in order to find tick positions in ordinal
  19437. // axes.
  19438. if (isXAxis && !secondPass) {
  19439. const hasExtremesChanged = axis.min !==
  19440. (axis.old && axis.old.min) ||
  19441. axis.max !== (axis.old && axis.old.max);
  19442. // First process all series assigned to that axis.
  19443. axis.series.forEach(function (series) {
  19444. // Allows filtering out points outside the plot area.
  19445. series.forceCrop = (series.forceCropping &&
  19446. series.forceCropping());
  19447. series.processData(hasExtremesChanged);
  19448. });
  19449. // Then apply grouping if needed. The hasExtremesChanged helps to
  19450. // decide if the data grouping should be skipped in the further
  19451. // calculations #16319.
  19452. fireEvent(this, 'postProcessData', { hasExtremesChanged });
  19453. }
  19454. // set the translation factor used in translate function
  19455. axis.setAxisTranslation();
  19456. // hook for ordinal axes and radial axes
  19457. fireEvent(this, 'initialAxisTranslation');
  19458. // In column-like charts, don't cramp in more ticks than there are
  19459. // points (#1943, #4184)
  19460. if (axis.pointRange && !tickIntervalOption) {
  19461. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  19462. }
  19463. // Before normalizing the tick interval, handle minimum tick interval.
  19464. // This applies only if tickInterval is not defined.
  19465. const minTickInterval = pick(options.minTickInterval,
  19466. // In datetime axes, don't go below the data interval, except when
  19467. // there are scatter-like series involved (#13369).
  19468. axis.dateTime &&
  19469. !axis.series.some((s) => s.noSharedTooltip) ?
  19470. axis.closestPointRange : 0);
  19471. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  19472. axis.tickInterval = minTickInterval;
  19473. }
  19474. // For linear axes, normalize the interval
  19475. if (!axis.dateTime && !axis.logarithmic && !tickIntervalOption) {
  19476. axis.tickInterval = getNormalizedTickInterval(axis, axis.tickInterval);
  19477. }
  19478. // Prevent ticks from getting so close that we can't draw the labels
  19479. if (!this.tickAmount) {
  19480. axis.tickInterval = axis.unsquish();
  19481. }
  19482. this.setTickPositions();
  19483. }
  19484. /**
  19485. * Now we have computed the normalized tickInterval, get the tick positions.
  19486. *
  19487. * @private
  19488. * @function Highcharts.Axis#setTickPositions
  19489. *
  19490. * @emits Highcharts.Axis#event:afterSetTickPositions
  19491. */
  19492. setTickPositions() {
  19493. const axis = this, options = this.options, tickPositionsOption = options.tickPositions, tickPositioner = options.tickPositioner, minorTickIntervalOption = this.getMinorTickInterval(), hasVerticalPanning = this.hasVerticalPanning(), isColorAxis = this.coll === 'colorAxis', startOnTick = ((isColorAxis || !hasVerticalPanning) && options.startOnTick), endOnTick = ((isColorAxis || !hasVerticalPanning) && options.endOnTick);
  19494. let tickPositions = [], tickPositionerResult;
  19495. // Set the tickmarkOffset
  19496. this.tickmarkOffset = (this.categories &&
  19497. options.tickmarkPlacement === 'between' &&
  19498. this.tickInterval === 1) ? 0.5 : 0; // #3202
  19499. // get minorTickInterval
  19500. this.minorTickInterval =
  19501. minorTickIntervalOption === 'auto' &&
  19502. this.tickInterval ?
  19503. this.tickInterval / options.minorTicksPerMajor :
  19504. minorTickIntervalOption;
  19505. // When there is only one point, or all points have the same value on
  19506. // this axis, then min and max are equal and tickPositions.length is 0
  19507. // or 1. In this case, add some padding in order to center the point,
  19508. // but leave it with one tick. #1337.
  19509. this.single =
  19510. this.min === this.max &&
  19511. defined(this.min) &&
  19512. !this.tickAmount &&
  19513. (
  19514. // Data is on integer (#6563)
  19515. parseInt(this.min, 10) === this.min ||
  19516. // Between integers and decimals are not allowed (#6274)
  19517. options.allowDecimals !== false);
  19518. /**
  19519. * Contains the current positions that are laid out on the axis. The
  19520. * positions are numbers in terms of axis values. In a category axis
  19521. * they are integers, in a datetime axis they are also integers, but
  19522. * designating milliseconds.
  19523. *
  19524. * This property is read only - for modifying the tick positions, use
  19525. * the `tickPositioner` callback or [axis.tickPositions(
  19526. * https://api.highcharts.com/highcharts/xAxis.tickPositions) option
  19527. * instead.
  19528. *
  19529. * @name Highcharts.Axis#tickPositions
  19530. * @type {Highcharts.AxisTickPositionsArray|undefined}
  19531. */
  19532. if (tickPositionsOption) {
  19533. // Find the tick positions. Work on a copy (#1565)
  19534. tickPositions = tickPositionsOption.slice();
  19535. }
  19536. else if (isNumber(this.min) && isNumber(this.max)) {
  19537. // Too many ticks (#6405). Create a friendly warning and provide two
  19538. // ticks so at least we can show the data series.
  19539. if ((!axis.ordinal || !axis.ordinal.positions) &&
  19540. ((this.max - this.min) /
  19541. this.tickInterval >
  19542. Math.max(2 * this.len, 200))) {
  19543. tickPositions = [this.min, this.max];
  19544. error(19, false, this.chart);
  19545. }
  19546. else if (axis.dateTime) {
  19547. tickPositions = axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, axis.ordinal && axis.ordinal.positions, this.closestPointRange, true);
  19548. }
  19549. else if (axis.logarithmic) {
  19550. tickPositions = axis.logarithmic.getLogTickPositions(this.tickInterval, this.min, this.max);
  19551. }
  19552. else {
  19553. const startingTickInterval = this.tickInterval;
  19554. let adjustedTickInterval = startingTickInterval;
  19555. while (adjustedTickInterval <= startingTickInterval * 2) {
  19556. tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
  19557. // If there are more tick positions than the set tickAmount,
  19558. // increase the tickInterval and continue until it fits.
  19559. // (#17100)
  19560. if (this.tickAmount &&
  19561. tickPositions.length > this.tickAmount) {
  19562. this.tickInterval = getNormalizedTickInterval(this, adjustedTickInterval *= 1.1);
  19563. }
  19564. else {
  19565. break;
  19566. }
  19567. }
  19568. }
  19569. // Too dense ticks, keep only the first and last (#4477)
  19570. if (tickPositions.length > this.len) {
  19571. tickPositions = [
  19572. tickPositions[0],
  19573. tickPositions[tickPositions.length - 1]
  19574. ];
  19575. // Reduce doubled value (#7339)
  19576. if (tickPositions[0] === tickPositions[1]) {
  19577. tickPositions.length = 1;
  19578. }
  19579. }
  19580. // Run the tick positioner callback, that allows modifying auto tick
  19581. // positions.
  19582. if (tickPositioner) {
  19583. // Make it available to the positioner
  19584. this.tickPositions = tickPositions;
  19585. tickPositionerResult = tickPositioner.apply(axis, [this.min, this.max]);
  19586. if (tickPositionerResult) {
  19587. tickPositions = tickPositionerResult;
  19588. }
  19589. }
  19590. }
  19591. this.tickPositions = tickPositions;
  19592. // Reset min/max or remove extremes based on start/end on tick
  19593. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  19594. this.trimTicks(tickPositions, startOnTick, endOnTick);
  19595. if (!this.isLinked && isNumber(this.min) && isNumber(this.max)) {
  19596. // Substract half a unit (#2619, #2846, #2515, #3390), but not in
  19597. // case of multiple ticks (#6897)
  19598. if (this.single &&
  19599. tickPositions.length < 2 &&
  19600. !this.categories &&
  19601. !this.series.some((s) => (s.is('heatmap') && s.options.pointPlacement === 'between'))) {
  19602. this.min -= 0.5;
  19603. this.max += 0.5;
  19604. }
  19605. if (!tickPositionsOption && !tickPositionerResult) {
  19606. this.adjustTickAmount();
  19607. }
  19608. }
  19609. fireEvent(this, 'afterSetTickPositions');
  19610. }
  19611. /**
  19612. * Handle startOnTick and endOnTick by either adapting to padding min/max or
  19613. * rounded min/max. Also handle single data points.
  19614. *
  19615. * @private
  19616. * @function Highcharts.Axis#trimTicks
  19617. *
  19618. * @param {Array<number>} tickPositions
  19619. * TO-DO: parameter description
  19620. *
  19621. * @param {boolean} [startOnTick]
  19622. * TO-DO: parameter description
  19623. *
  19624. * @param {boolean} [endOnTick]
  19625. * TO-DO: parameter description
  19626. */
  19627. trimTicks(tickPositions, startOnTick, endOnTick) {
  19628. const roundedMin = tickPositions[0], roundedMax = tickPositions[tickPositions.length - 1], minPointOffset = (!this.isOrdinal && this.minPointOffset) || 0; // (#12716)
  19629. fireEvent(this, 'trimTicks');
  19630. if (!this.isLinked) {
  19631. if (startOnTick && roundedMin !== -Infinity) { // #6502
  19632. this.min = roundedMin;
  19633. }
  19634. else {
  19635. while (this.min - minPointOffset > tickPositions[0]) {
  19636. tickPositions.shift();
  19637. }
  19638. }
  19639. if (endOnTick) {
  19640. this.max = roundedMax;
  19641. }
  19642. else {
  19643. while (this.max + minPointOffset <
  19644. tickPositions[tickPositions.length - 1]) {
  19645. tickPositions.pop();
  19646. }
  19647. }
  19648. // If no tick are left, set one tick in the middle (#3195)
  19649. if (tickPositions.length === 0 &&
  19650. defined(roundedMin) &&
  19651. !this.options.tickPositions) {
  19652. tickPositions.push((roundedMax + roundedMin) / 2);
  19653. }
  19654. }
  19655. }
  19656. /**
  19657. * Check if there are multiple axes in the same pane.
  19658. *
  19659. * @private
  19660. * @function Highcharts.Axis#alignToOthers
  19661. *
  19662. * @return {boolean|undefined}
  19663. * True if there are other axes.
  19664. */
  19665. alignToOthers() {
  19666. const axis = this, alignedAxes = [this], options = axis.options, alignThresholds = (this.coll === 'yAxis' &&
  19667. this.chart.options.chart.alignThresholds), thresholdAlignments = [];
  19668. let hasOther;
  19669. axis.thresholdAlignment = void 0;
  19670. if ((
  19671. // Only if alignTicks or alignThresholds is true
  19672. (this.chart.options.chart.alignTicks !== false &&
  19673. options.alignTicks) || (alignThresholds)) &&
  19674. // Disabled when startOnTick or endOnTick are false (#7604)
  19675. options.startOnTick !== false &&
  19676. options.endOnTick !== false &&
  19677. // Don't try to align ticks on a log axis, they are not evenly
  19678. // spaced (#6021)
  19679. !axis.logarithmic) {
  19680. // Get a key identifying which pane the axis belongs to
  19681. const getKey = (axis) => {
  19682. const { horiz, options } = axis;
  19683. return [
  19684. horiz ? options.left : options.top,
  19685. options.width,
  19686. options.height,
  19687. options.pane
  19688. ].join(',');
  19689. };
  19690. const thisKey = getKey(this);
  19691. this.chart[this.coll].forEach(function (otherAxis) {
  19692. const { series } = otherAxis;
  19693. if (
  19694. // #4442
  19695. series.length &&
  19696. series.some((s) => s.visible) &&
  19697. otherAxis !== axis &&
  19698. getKey(otherAxis) === thisKey) {
  19699. hasOther = true; // #4201
  19700. alignedAxes.push(otherAxis);
  19701. }
  19702. });
  19703. }
  19704. if (hasOther && alignThresholds) {
  19705. // Handle alignThresholds. The `thresholdAlignments` array keeps
  19706. // records of where each axis in the group wants its threshold, from
  19707. // 0 which is on `axis.min`, to 1 which is on `axis.max`.
  19708. alignedAxes.forEach((otherAxis) => {
  19709. const threshAlign = otherAxis.getThresholdAlignment(axis);
  19710. if (isNumber(threshAlign)) {
  19711. thresholdAlignments.push(threshAlign);
  19712. }
  19713. });
  19714. // For each of the axes in the group, record the average
  19715. // `thresholdAlignment`.
  19716. const thresholdAlignment = thresholdAlignments.length > 1 ?
  19717. thresholdAlignments.reduce((sum, n) => (sum += n), 0) / thresholdAlignments.length :
  19718. void 0;
  19719. alignedAxes.forEach((axis) => {
  19720. axis.thresholdAlignment = thresholdAlignment;
  19721. });
  19722. }
  19723. return hasOther;
  19724. }
  19725. /**
  19726. * Where the axis wants its threshold, from 0 which is on `axis.min`, to 1 which
  19727. * is on `axis.max`.
  19728. *
  19729. * @private
  19730. * @function Highcharts.Axis#getThresholdAlignment
  19731. */
  19732. getThresholdAlignment(callerAxis) {
  19733. if (!isNumber(this.dataMin) ||
  19734. (this !== callerAxis &&
  19735. this.series.some((s) => (s.isDirty || s.isDirtyData)))) {
  19736. this.getSeriesExtremes();
  19737. }
  19738. if (isNumber(this.threshold)) {
  19739. let thresholdAlignment = clamp(((this.threshold - (this.dataMin || 0)) /
  19740. ((this.dataMax || 0) - (this.dataMin || 0))), 0, 1);
  19741. if (this.options.reversed) {
  19742. thresholdAlignment = 1 - thresholdAlignment;
  19743. }
  19744. return thresholdAlignment;
  19745. }
  19746. }
  19747. /**
  19748. * Find the max ticks of either the x and y axis collection, and record it
  19749. * in `this.tickAmount`.
  19750. *
  19751. * @private
  19752. * @function Highcharts.Axis#getTickAmount
  19753. */
  19754. getTickAmount() {
  19755. const axis = this, options = this.options, tickPixelInterval = options.tickPixelInterval;
  19756. let tickAmount = options.tickAmount;
  19757. if (!defined(options.tickInterval) &&
  19758. !tickAmount &&
  19759. this.len < tickPixelInterval &&
  19760. !this.isRadial &&
  19761. !axis.logarithmic &&
  19762. options.startOnTick &&
  19763. options.endOnTick) {
  19764. tickAmount = 2;
  19765. }
  19766. if (!tickAmount && this.alignToOthers()) {
  19767. // Add 1 because 4 tick intervals require 5 ticks (including first
  19768. // and last)
  19769. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  19770. }
  19771. // For tick amounts of 2 and 3, compute five ticks and remove the
  19772. // intermediate ones. This prevents the axis from adding ticks that are
  19773. // too far away from the data extremes.
  19774. if (tickAmount < 4) {
  19775. this.finalTickAmt = tickAmount;
  19776. tickAmount = 5;
  19777. }
  19778. this.tickAmount = tickAmount;
  19779. }
  19780. /**
  19781. * When using multiple axes, adjust the number of ticks to match the highest
  19782. * number of ticks in that group.
  19783. *
  19784. * @private
  19785. * @function Highcharts.Axis#adjustTickAmount
  19786. */
  19787. adjustTickAmount() {
  19788. const axis = this, { finalTickAmt, max, min, options, tickPositions, tickAmount, thresholdAlignment } = axis, currentTickAmount = tickPositions && tickPositions.length, threshold = pick(axis.threshold, axis.softThreshold ? 0 : null);
  19789. let len, i, tickInterval = axis.tickInterval, thresholdTickIndex;
  19790. const
  19791. // Extend the tickPositions by appending a position
  19792. append = () => tickPositions.push(correctFloat(tickPositions[tickPositions.length - 1] +
  19793. tickInterval)),
  19794. // Extend the tickPositions by prepending a position
  19795. prepend = () => tickPositions.unshift(correctFloat(tickPositions[0] - tickInterval));
  19796. // If `thresholdAlignment` is a number, it means the `alignThresholds`
  19797. // option is true. The `thresholdAlignment` is a scalar value between 0
  19798. // and 1 for where the threshold should be relative to `axis.min` and
  19799. // `axis.max`. Now that we know the tick amount, convert this to the
  19800. // tick index. Unless `thresholdAlignment` is exactly 0 or 1, avoid the
  19801. // first or last tick because that would lead to series being clipped.
  19802. if (isNumber(thresholdAlignment)) {
  19803. thresholdTickIndex = thresholdAlignment < 0.5 ?
  19804. Math.ceil(thresholdAlignment * (tickAmount - 1)) :
  19805. Math.floor(thresholdAlignment * (tickAmount - 1));
  19806. if (options.reversed) {
  19807. thresholdTickIndex = tickAmount - 1 - thresholdTickIndex;
  19808. }
  19809. }
  19810. if (axis.hasData() && isNumber(min) && isNumber(max)) { // #14769
  19811. // Adjust extremes and translation to the modified tick positions
  19812. const adjustExtremes = () => {
  19813. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  19814. // Do not crop when ticks are not extremes (#9841)
  19815. axis.min = options.startOnTick ?
  19816. tickPositions[0] :
  19817. Math.min(min, tickPositions[0]);
  19818. axis.max = options.endOnTick ?
  19819. tickPositions[tickPositions.length - 1] :
  19820. Math.max(max, tickPositions[tickPositions.length - 1]);
  19821. };
  19822. // When the axis is subject to the alignThresholds option. Use
  19823. // axis.threshold because the local threshold includes the
  19824. // `softThreshold`.
  19825. if (isNumber(thresholdTickIndex) && isNumber(axis.threshold)) {
  19826. // Throw away the previously computed tickPositions and start
  19827. // from scratch with only the threshold itself, then add ticks
  19828. // below the threshold first, then fill up above the threshold.
  19829. // If we are not able to fill up to axis.max, double the
  19830. // tickInterval and run again.
  19831. while (tickPositions[thresholdTickIndex] !== threshold ||
  19832. tickPositions.length !== tickAmount ||
  19833. tickPositions[0] > min ||
  19834. tickPositions[tickPositions.length - 1] < max) {
  19835. tickPositions.length = 0;
  19836. tickPositions.push(axis.threshold);
  19837. while (tickPositions.length < tickAmount) {
  19838. if (
  19839. // Start by prepending positions until the threshold
  19840. // is at the required index...
  19841. tickPositions[thresholdTickIndex] === void 0 ||
  19842. tickPositions[thresholdTickIndex] > axis.threshold) {
  19843. prepend();
  19844. }
  19845. else {
  19846. // ... then append positions until we have the
  19847. // required length
  19848. append();
  19849. }
  19850. }
  19851. // Safety vent
  19852. if (tickInterval > axis.tickInterval * 8) {
  19853. break;
  19854. }
  19855. tickInterval *= 2;
  19856. }
  19857. adjustExtremes();
  19858. }
  19859. else if (currentTickAmount < tickAmount) {
  19860. while (tickPositions.length < tickAmount) {
  19861. // Extend evenly for both sides unless we're on the
  19862. // threshold (#3965)
  19863. if (tickPositions.length % 2 || min === threshold) {
  19864. append();
  19865. }
  19866. else {
  19867. prepend();
  19868. }
  19869. }
  19870. adjustExtremes();
  19871. }
  19872. // The finalTickAmt property is set in getTickAmount
  19873. if (defined(finalTickAmt)) {
  19874. i = len = tickPositions.length;
  19875. while (i--) {
  19876. if (
  19877. // Remove every other tick
  19878. (finalTickAmt === 3 && i % 2 === 1) ||
  19879. // Remove all but first and last
  19880. (finalTickAmt <= 2 && i > 0 && i < len - 1)) {
  19881. tickPositions.splice(i, 1);
  19882. }
  19883. }
  19884. axis.finalTickAmt = void 0;
  19885. }
  19886. }
  19887. }
  19888. /**
  19889. * Set the scale based on data min and max, user set min and max or options.
  19890. *
  19891. * @private
  19892. * @function Highcharts.Axis#setScale
  19893. *
  19894. * @emits Highcharts.Axis#event:afterSetScale
  19895. */
  19896. setScale() {
  19897. const axis = this;
  19898. let isDirtyData = false, isXAxisDirty = false;
  19899. axis.series.forEach(function (series) {
  19900. isDirtyData = isDirtyData || series.isDirtyData || series.isDirty;
  19901. // When x axis is dirty, we need new data extremes for y as
  19902. // well:
  19903. isXAxisDirty = (isXAxisDirty ||
  19904. (series.xAxis && series.xAxis.isDirty) ||
  19905. false);
  19906. });
  19907. // set the new axisLength
  19908. axis.setAxisSize();
  19909. const isDirtyAxisLength = axis.len !== (axis.old && axis.old.len);
  19910. // do we really need to go through all this?
  19911. if (isDirtyAxisLength ||
  19912. isDirtyData ||
  19913. isXAxisDirty ||
  19914. axis.isLinked ||
  19915. axis.forceRedraw ||
  19916. axis.userMin !== (axis.old && axis.old.userMin) ||
  19917. axis.userMax !== (axis.old && axis.old.userMax) ||
  19918. axis.alignToOthers()) {
  19919. if (axis.stacking) {
  19920. axis.stacking.resetStacks();
  19921. axis.stacking.buildStacks();
  19922. }
  19923. axis.forceRedraw = false;
  19924. // #18066 delete minRange property to ensure that it will be
  19925. // calculated again after dirty data in series
  19926. if (!axis.userMinRange) {
  19927. axis.minRange = void 0;
  19928. }
  19929. // get data extremes if needed
  19930. axis.getSeriesExtremes();
  19931. // get fixed positions based on tickInterval
  19932. axis.setTickInterval();
  19933. // Mark as dirty if it is not already set to dirty and extremes have
  19934. // changed. #595.
  19935. if (!axis.isDirty) {
  19936. axis.isDirty =
  19937. isDirtyAxisLength ||
  19938. axis.min !== (axis.old && axis.old.min) ||
  19939. axis.max !== (axis.old && axis.old.max);
  19940. }
  19941. }
  19942. else if (axis.stacking) {
  19943. axis.stacking.cleanStacks();
  19944. }
  19945. // Recalculate panning state object, when the data
  19946. // has changed. It is required when vertical panning is enabled.
  19947. if (isDirtyData && axis.panningState) {
  19948. axis.panningState.isDirty = true;
  19949. }
  19950. fireEvent(this, 'afterSetScale');
  19951. }
  19952. /**
  19953. * Set the minimum and maximum of the axes after render time. If the
  19954. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  19955. * values are rounded off to the nearest tick. To prevent this, these
  19956. * options can be set to false before calling setExtremes. Also, setExtremes
  19957. * will not allow a range lower than the `minRange` option, which by default
  19958. * is the range of five points.
  19959. *
  19960. * @sample highcharts/members/axis-setextremes/
  19961. * Set extremes from a button
  19962. * @sample highcharts/members/axis-setextremes-datetime/
  19963. * Set extremes on a datetime axis
  19964. * @sample highcharts/members/axis-setextremes-off-ticks/
  19965. * Set extremes off ticks
  19966. * @sample stock/members/axis-setextremes/
  19967. * Set extremes in Highcharts Stock
  19968. *
  19969. * @function Highcharts.Axis#setExtremes
  19970. *
  19971. * @param {number} [newMin]
  19972. * The new minimum value.
  19973. *
  19974. * @param {number} [newMax]
  19975. * The new maximum value.
  19976. *
  19977. * @param {boolean} [redraw=true]
  19978. * Whether to redraw the chart or wait for an explicit call to
  19979. * {@link Highcharts.Chart#redraw}
  19980. *
  19981. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  19982. * Enable or modify animations.
  19983. *
  19984. * @param {*} [eventArguments]
  19985. * Arguments to be accessed in event handler.
  19986. *
  19987. * @emits Highcharts.Axis#event:setExtremes
  19988. */
  19989. setExtremes(newMin, newMax, redraw, animation, eventArguments) {
  19990. const axis = this, chart = axis.chart;
  19991. redraw = pick(redraw, true); // defaults to true
  19992. axis.series.forEach(function (serie) {
  19993. delete serie.kdTree;
  19994. });
  19995. // Extend the arguments with min and max
  19996. eventArguments = extend(eventArguments, {
  19997. min: newMin,
  19998. max: newMax
  19999. });
  20000. // Fire the event
  20001. fireEvent(axis, 'setExtremes', eventArguments, function () {
  20002. axis.userMin = newMin;
  20003. axis.userMax = newMax;
  20004. axis.eventArgs = eventArguments;
  20005. if (redraw) {
  20006. chart.redraw(animation);
  20007. }
  20008. });
  20009. }
  20010. /**
  20011. * Overridable method for zooming chart. Pulled out in a separate method to
  20012. * allow overriding in stock charts.
  20013. *
  20014. * @private
  20015. * @function Highcharts.Axis#zoom
  20016. */
  20017. zoom(newMin, newMax) {
  20018. const axis = this, dataMin = this.dataMin, dataMax = this.dataMax, options = this.options, min = Math.min(dataMin, pick(options.min, dataMin)), max = Math.max(dataMax, pick(options.max, dataMax)), evt = {
  20019. newMin: newMin,
  20020. newMax: newMax
  20021. };
  20022. fireEvent(this, 'zoom', evt, function (e) {
  20023. // Use e.newMin and e.newMax - event handlers may have altered them
  20024. let newMin = e.newMin, newMax = e.newMax;
  20025. if (newMin !== axis.min || newMax !== axis.max) { // #5790
  20026. // Prevent pinch zooming out of range. Check for defined is for
  20027. // #1946. #1734.
  20028. if (!axis.allowZoomOutside) {
  20029. // #6014, sometimes newMax will be smaller than min (or
  20030. // newMin will be larger than max).
  20031. if (defined(dataMin)) {
  20032. if (newMin < min) {
  20033. newMin = min;
  20034. }
  20035. if (newMin > max) {
  20036. newMin = max;
  20037. }
  20038. }
  20039. if (defined(dataMax)) {
  20040. if (newMax < min) {
  20041. newMax = min;
  20042. }
  20043. if (newMax > max) {
  20044. newMax = max;
  20045. }
  20046. }
  20047. }
  20048. // In full view, displaying the reset zoom button is not
  20049. // required
  20050. axis.displayBtn = (typeof newMin !== 'undefined' ||
  20051. typeof newMax !== 'undefined');
  20052. // Do it
  20053. axis.setExtremes(newMin, newMax, false, void 0, { trigger: 'zoom' });
  20054. }
  20055. e.zoomed = true;
  20056. });
  20057. return evt.zoomed;
  20058. }
  20059. /**
  20060. * Update the axis metrics.
  20061. *
  20062. * @private
  20063. * @function Highcharts.Axis#setAxisSize
  20064. */
  20065. setAxisSize() {
  20066. const chart = this.chart, options = this.options,
  20067. // [top, right, bottom, left]
  20068. offsets = options.offsets || [0, 0, 0, 0], horiz = this.horiz,
  20069. // Check for percentage based input values. Rounding fixes problems
  20070. // with column overflow and plot line filtering (#4898, #4899)
  20071. width = this.width = Math.round(relativeLength(pick(options.width, chart.plotWidth - offsets[3] + offsets[1]), chart.plotWidth)), height = this.height = Math.round(relativeLength(pick(options.height, chart.plotHeight - offsets[0] + offsets[2]), chart.plotHeight)), top = this.top = Math.round(relativeLength(pick(options.top, chart.plotTop + offsets[0]), chart.plotHeight, chart.plotTop)), left = this.left = Math.round(relativeLength(pick(options.left, chart.plotLeft + offsets[3]), chart.plotWidth, chart.plotLeft));
  20072. // Expose basic values to use in Series object and navigator
  20073. this.bottom = chart.chartHeight - height - top;
  20074. this.right = chart.chartWidth - width - left;
  20075. // Direction agnostic properties
  20076. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  20077. this.pos = horiz ? left : top; // distance from SVG origin
  20078. }
  20079. /**
  20080. * Get the current extremes for the axis.
  20081. *
  20082. * @sample highcharts/members/axis-getextremes/
  20083. * Report extremes by click on a button
  20084. *
  20085. * @function Highcharts.Axis#getExtremes
  20086. *
  20087. * @return {Highcharts.ExtremesObject}
  20088. * An object containing extremes information.
  20089. */
  20090. getExtremes() {
  20091. const axis = this, log = axis.logarithmic;
  20092. return {
  20093. min: log ?
  20094. correctFloat(log.lin2log(axis.min)) :
  20095. axis.min,
  20096. max: log ?
  20097. correctFloat(log.lin2log(axis.max)) :
  20098. axis.max,
  20099. dataMin: axis.dataMin,
  20100. dataMax: axis.dataMax,
  20101. userMin: axis.userMin,
  20102. userMax: axis.userMax
  20103. };
  20104. }
  20105. /**
  20106. * Get the zero plane either based on zero or on the min or max value.
  20107. * Used in bar and area plots.
  20108. *
  20109. * @function Highcharts.Axis#getThreshold
  20110. *
  20111. * @param {number} threshold
  20112. * The threshold in axis values.
  20113. *
  20114. * @return {number}
  20115. * The translated threshold position in terms of pixels, and corrected to
  20116. * stay within the axis bounds.
  20117. */
  20118. getThreshold(threshold) {
  20119. const axis = this, log = axis.logarithmic, realMin = log ? log.lin2log(axis.min) : axis.min, realMax = log ? log.lin2log(axis.max) : axis.max;
  20120. if (threshold === null || threshold === -Infinity) {
  20121. threshold = realMin;
  20122. }
  20123. else if (threshold === Infinity) {
  20124. threshold = realMax;
  20125. }
  20126. else if (realMin > threshold) {
  20127. threshold = realMin;
  20128. }
  20129. else if (realMax < threshold) {
  20130. threshold = realMax;
  20131. }
  20132. return axis.translate(threshold, 0, 1, 0, 1);
  20133. }
  20134. /**
  20135. * Compute auto alignment for the axis label based on which side the axis is
  20136. * on and the given rotation for the label.
  20137. *
  20138. * @private
  20139. * @function Highcharts.Axis#autoLabelAlign
  20140. *
  20141. * @param {number} rotation
  20142. * The rotation in degrees as set by either the `rotation` or `autoRotation`
  20143. * options.
  20144. *
  20145. * @return {Highcharts.AlignValue}
  20146. * Can be `"center"`, `"left"` or `"right"`.
  20147. */
  20148. autoLabelAlign(rotation) {
  20149. const angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360, evt = { align: 'center' };
  20150. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  20151. if (angle > 15 && angle < 165) {
  20152. e.align = 'right';
  20153. }
  20154. else if (angle > 195 && angle < 345) {
  20155. e.align = 'left';
  20156. }
  20157. });
  20158. return evt.align;
  20159. }
  20160. /**
  20161. * Get the tick length and width for the axis based on axis options.
  20162. *
  20163. * @private
  20164. * @function Highcharts.Axis#tickSize
  20165. *
  20166. * @param {string} [prefix]
  20167. * 'tick' or 'minorTick'
  20168. *
  20169. * @return {Array<number,number>|undefined}
  20170. * An array of tickLength and tickWidth
  20171. */
  20172. tickSize(prefix) {
  20173. const options = this.options, tickWidth = pick(options[prefix === 'tick' ? 'tickWidth' : 'minorTickWidth'],
  20174. // Default to 1 on linear and datetime X axes
  20175. prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0);
  20176. let tickLength = options[prefix === 'tick' ? 'tickLength' : 'minorTickLength'], tickSize;
  20177. if (tickWidth && tickLength) {
  20178. // Negate the length
  20179. if (options[prefix + 'Position'] === 'inside') {
  20180. tickLength = -tickLength;
  20181. }
  20182. tickSize = [tickLength, tickWidth];
  20183. }
  20184. const e = { tickSize };
  20185. fireEvent(this, 'afterTickSize', e);
  20186. return e.tickSize;
  20187. }
  20188. /**
  20189. * Return the size of the labels.
  20190. *
  20191. * @private
  20192. * @function Highcharts.Axis#labelMetrics
  20193. */
  20194. labelMetrics() {
  20195. const renderer = this.chart.renderer, ticks = this.ticks, tick = ticks[Object.keys(ticks)[0]] || {};
  20196. return this.chart.renderer.fontMetrics(tick.label ||
  20197. tick.movedLabel ||
  20198. renderer.box);
  20199. }
  20200. /**
  20201. * Prevent the ticks from getting so close we can't draw the labels. On a
  20202. * horizontal axis, this is handled by rotating the labels, removing ticks
  20203. * and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  20204. *
  20205. * @private
  20206. * @function Highcharts.Axis#unsquish
  20207. */
  20208. unsquish() {
  20209. const labelOptions = this.options.labels, horiz = this.horiz, tickInterval = this.tickInterval, slotSize = this.len / (((this.categories ? 1 : 0) +
  20210. this.max -
  20211. this.min) /
  20212. tickInterval), rotationOption = labelOptions.rotation,
  20213. // We don't know the actual rendered line height at this point, but
  20214. // it defaults to 0.75em
  20215. lineHeight = this.labelMetrics().h * 0.75, range = Math.max(this.max - this.min, 0),
  20216. // Return the multiple of tickInterval that is needed to avoid
  20217. // collision
  20218. getStep = function (spaceNeeded) {
  20219. let step = spaceNeeded / (slotSize || 1);
  20220. step = step > 1 ? Math.ceil(step) : 1;
  20221. // Guard for very small or negative angles (#9835)
  20222. if (step * tickInterval > range &&
  20223. spaceNeeded !== Infinity &&
  20224. slotSize !== Infinity &&
  20225. range) {
  20226. step = Math.ceil(range / tickInterval);
  20227. }
  20228. return correctFloat(step * tickInterval);
  20229. };
  20230. let newTickInterval = tickInterval, rotation, bestScore = Number.MAX_VALUE, autoRotation;
  20231. if (horiz) {
  20232. if (!labelOptions.staggerLines) {
  20233. if (isNumber(rotationOption)) {
  20234. autoRotation = [rotationOption];
  20235. }
  20236. else if (slotSize < labelOptions.autoRotationLimit) {
  20237. autoRotation = labelOptions.autoRotation;
  20238. }
  20239. }
  20240. if (autoRotation) {
  20241. let step, score;
  20242. // Loop over the given autoRotation options, and determine which
  20243. // gives the best score. The best score is that with the lowest
  20244. // number of steps and a rotation closest to horizontal.
  20245. for (const rot of autoRotation) {
  20246. if (rot === rotationOption ||
  20247. (rot && rot >= -90 && rot <= 90)) { // #3891
  20248. step = getStep(Math.abs(lineHeight / Math.sin(deg2rad * rot)));
  20249. score = step + Math.abs(rot / 360);
  20250. if (score < bestScore) {
  20251. bestScore = score;
  20252. rotation = rot;
  20253. newTickInterval = step;
  20254. }
  20255. }
  20256. }
  20257. }
  20258. }
  20259. else { // #4411
  20260. newTickInterval = getStep(lineHeight);
  20261. }
  20262. this.autoRotation = autoRotation;
  20263. this.labelRotation = pick(rotation, isNumber(rotationOption) ? rotationOption : 0);
  20264. return labelOptions.step ? tickInterval : newTickInterval;
  20265. }
  20266. /**
  20267. * Get the general slot width for labels/categories on this axis. This may
  20268. * change between the pre-render (from Axis.getOffset) and the final tick
  20269. * rendering and placement.
  20270. *
  20271. * @private
  20272. * @function Highcharts.Axis#getSlotWidth
  20273. *
  20274. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  20275. * basing on tick label. It is used in highcharts-3d module, where the slots
  20276. * has different widths depending on perspective angles.
  20277. *
  20278. * @return {number}
  20279. * The pixel width allocated to each axis label.
  20280. */
  20281. getSlotWidth(tick) {
  20282. // #5086, #1580, #1931
  20283. const chart = this.chart, horiz = this.horiz, labelOptions = this.options.labels, slotCount = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1), marginLeft = chart.margin[3];
  20284. // Used by grid axis
  20285. if (tick && isNumber(tick.slotWidth)) { // #13221, can be 0
  20286. return tick.slotWidth;
  20287. }
  20288. if (horiz && labelOptions.step < 2) {
  20289. if (labelOptions.rotation) { // #4415
  20290. return 0;
  20291. }
  20292. return ((this.staggerLines || 1) * this.len) / slotCount;
  20293. }
  20294. if (!horiz) {
  20295. // #7028
  20296. const cssWidth = labelOptions.style.width;
  20297. if (cssWidth !== void 0) {
  20298. return parseInt(String(cssWidth), 10);
  20299. }
  20300. if (marginLeft) {
  20301. return marginLeft - chart.spacing[3];
  20302. }
  20303. }
  20304. // Last resort, a fraction of the available size
  20305. return chart.chartWidth * 0.33;
  20306. }
  20307. /**
  20308. * Render the axis labels and determine whether ellipsis or rotation need to
  20309. * be applied.
  20310. *
  20311. * @private
  20312. * @function Highcharts.Axis#renderUnsquish
  20313. */
  20314. renderUnsquish() {
  20315. const chart = this.chart, renderer = chart.renderer, tickPositions = this.tickPositions, ticks = this.ticks, labelOptions = this.options.labels, labelStyleOptions = labelOptions.style, horiz = this.horiz, slotWidth = this.getSlotWidth(), innerWidth = Math.max(1, Math.round(slotWidth - 2 * labelOptions.padding)), attr = {}, labelMetrics = this.labelMetrics(), textOverflowOption = labelStyleOptions.textOverflow;
  20316. let commonWidth, commonTextOverflow, maxLabelLength = 0, label, i, pos;
  20317. // Set rotation option unless it is "auto", like in gauges
  20318. if (!isString(labelOptions.rotation)) {
  20319. // #4443
  20320. attr.rotation = labelOptions.rotation || 0;
  20321. }
  20322. // Get the longest label length
  20323. tickPositions.forEach(function (tickPosition) {
  20324. const tick = ticks[tickPosition];
  20325. // Replace label - sorting animation
  20326. if (tick.movedLabel) {
  20327. tick.replaceMovedLabel();
  20328. }
  20329. if (tick &&
  20330. tick.label &&
  20331. tick.label.textPxLength > maxLabelLength) {
  20332. maxLabelLength = tick.label.textPxLength;
  20333. }
  20334. });
  20335. this.maxLabelLength = maxLabelLength;
  20336. // Handle auto rotation on horizontal axis
  20337. if (this.autoRotation) {
  20338. // Apply rotation only if the label is too wide for the slot, and
  20339. // the label is wider than its height.
  20340. if (maxLabelLength > innerWidth &&
  20341. maxLabelLength > labelMetrics.h) {
  20342. attr.rotation = this.labelRotation;
  20343. }
  20344. else {
  20345. this.labelRotation = 0;
  20346. }
  20347. // Handle word-wrap or ellipsis on vertical axis
  20348. }
  20349. else if (slotWidth) {
  20350. // For word-wrap or ellipsis
  20351. commonWidth = innerWidth;
  20352. if (!textOverflowOption) {
  20353. commonTextOverflow = 'clip';
  20354. // On vertical axis, only allow word wrap if there is room
  20355. // for more lines.
  20356. i = tickPositions.length;
  20357. while (!horiz && i--) {
  20358. pos = tickPositions[i];
  20359. label = ticks[pos].label;
  20360. if (label) {
  20361. // Reset ellipsis in order to get the correct
  20362. // bounding box (#4070)
  20363. if (label.styles &&
  20364. label.styles.textOverflow === 'ellipsis') {
  20365. label.css({ textOverflow: 'clip' });
  20366. // Set the correct width in order to read
  20367. // the bounding box height (#4678, #5034)
  20368. }
  20369. else if (label.textPxLength > slotWidth) {
  20370. label.css({ width: slotWidth + 'px' });
  20371. }
  20372. if (label.getBBox().height > (this.len / tickPositions.length -
  20373. (labelMetrics.h - labelMetrics.f))) {
  20374. label.specificTextOverflow = 'ellipsis';
  20375. }
  20376. }
  20377. }
  20378. }
  20379. }
  20380. // Add ellipsis if the label length is significantly longer than ideal
  20381. if (attr.rotation) {
  20382. commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
  20383. chart.chartHeight * 0.33 :
  20384. maxLabelLength);
  20385. if (!textOverflowOption) {
  20386. commonTextOverflow = 'ellipsis';
  20387. }
  20388. }
  20389. // Set the explicit or automatic label alignment
  20390. this.labelAlign = labelOptions.align ||
  20391. this.autoLabelAlign(this.labelRotation);
  20392. if (this.labelAlign) {
  20393. attr.align = this.labelAlign;
  20394. }
  20395. // Apply general and specific CSS
  20396. tickPositions.forEach(function (pos) {
  20397. const tick = ticks[pos], label = tick && tick.label, widthOption = labelStyleOptions.width, css = {};
  20398. if (label) {
  20399. // This needs to go before the CSS in old IE (#4502)
  20400. label.attr(attr);
  20401. if (tick.shortenLabel) {
  20402. tick.shortenLabel();
  20403. }
  20404. else if (commonWidth &&
  20405. !widthOption &&
  20406. // Setting width in this case messes with the bounding box
  20407. // (#7975)
  20408. labelStyleOptions.whiteSpace !== 'nowrap' &&
  20409. (
  20410. // Speed optimizing, #7656
  20411. commonWidth < label.textPxLength ||
  20412. // Resetting CSS, #4928
  20413. label.element.tagName === 'SPAN')) {
  20414. css.width = commonWidth + 'px';
  20415. if (!textOverflowOption) {
  20416. css.textOverflow = (label.specificTextOverflow ||
  20417. commonTextOverflow);
  20418. }
  20419. label.css(css);
  20420. // Reset previously shortened label (#8210)
  20421. }
  20422. else if (label.styles &&
  20423. label.styles.width &&
  20424. !css.width &&
  20425. !widthOption) {
  20426. label.css({ width: null });
  20427. }
  20428. delete label.specificTextOverflow;
  20429. tick.rotation = attr.rotation;
  20430. }
  20431. }, this);
  20432. // Note: Why is this not part of getLabelPosition?
  20433. this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
  20434. }
  20435. /**
  20436. * Return true if the axis has associated data.
  20437. *
  20438. * @function Highcharts.Axis#hasData
  20439. *
  20440. * @return {boolean}
  20441. * True if the axis has associated visible series and those series have
  20442. * either valid data points or explicit `min` and `max` settings.
  20443. */
  20444. hasData() {
  20445. return this.series.some(function (s) {
  20446. return s.hasData();
  20447. }) ||
  20448. (this.options.showEmpty &&
  20449. defined(this.min) &&
  20450. defined(this.max));
  20451. }
  20452. /**
  20453. * Adds the title defined in axis.options.title.
  20454. *
  20455. * @function Highcharts.Axis#addTitle
  20456. *
  20457. * @param {boolean} [display]
  20458. * Whether or not to display the title.
  20459. */
  20460. addTitle(display) {
  20461. const axis = this, renderer = axis.chart.renderer, horiz = axis.horiz, opposite = axis.opposite, options = axis.options, axisTitleOptions = options.title, styledMode = axis.chart.styledMode;
  20462. let textAlign;
  20463. if (!axis.axisTitle) {
  20464. textAlign = axisTitleOptions.textAlign;
  20465. if (!textAlign) {
  20466. textAlign = (horiz ? {
  20467. low: 'left',
  20468. middle: 'center',
  20469. high: 'right'
  20470. } : {
  20471. low: opposite ? 'right' : 'left',
  20472. middle: 'center',
  20473. high: opposite ? 'left' : 'right'
  20474. })[axisTitleOptions.align];
  20475. }
  20476. axis.axisTitle = renderer
  20477. .text(axisTitleOptions.text || '', 0, 0, axisTitleOptions.useHTML)
  20478. .attr({
  20479. zIndex: 7,
  20480. rotation: axisTitleOptions.rotation,
  20481. align: textAlign
  20482. })
  20483. .addClass('highcharts-axis-title');
  20484. // #7814, don't mutate style option
  20485. if (!styledMode) {
  20486. axis.axisTitle.css(merge(axisTitleOptions.style));
  20487. }
  20488. axis.axisTitle.add(axis.axisGroup);
  20489. axis.axisTitle.isNew = true;
  20490. }
  20491. // Max width defaults to the length of the axis
  20492. if (!styledMode &&
  20493. !axisTitleOptions.style.width &&
  20494. !axis.isRadial) {
  20495. axis.axisTitle.css({
  20496. width: axis.len + 'px'
  20497. });
  20498. }
  20499. // hide or show the title depending on whether showEmpty is set
  20500. axis.axisTitle[display ? 'show' : 'hide'](display);
  20501. }
  20502. /**
  20503. * Generates a tick for initial positioning.
  20504. *
  20505. * @private
  20506. * @function Highcharts.Axis#generateTick
  20507. *
  20508. * @param {number} pos
  20509. * The tick position in axis values.
  20510. *
  20511. * @param {number} [i]
  20512. * The index of the tick in {@link Axis.tickPositions}.
  20513. */
  20514. generateTick(pos) {
  20515. const axis = this, ticks = axis.ticks;
  20516. if (!ticks[pos]) {
  20517. ticks[pos] = new Tick(axis, pos);
  20518. }
  20519. else {
  20520. ticks[pos].addLabel(); // update labels depending on tick interval
  20521. }
  20522. }
  20523. /**
  20524. * Render the tick labels to a preliminary position to get their sizes
  20525. *
  20526. * @private
  20527. * @function Highcharts.Axis#getOffset
  20528. *
  20529. * @emits Highcharts.Axis#event:afterGetOffset
  20530. */
  20531. getOffset() {
  20532. const axis = this, { chart, horiz, options, side, ticks, tickPositions, coll, axisParent // Used in color axis
  20533. } = axis, renderer = chart.renderer, invertedSide = (chart.inverted && !axis.isZAxis ?
  20534. [1, 0, 3, 2][side] :
  20535. side), hasData = axis.hasData(), axisTitleOptions = options.title, labelOptions = options.labels, hasCrossing = isNumber(options.crossing), axisOffset = chart.axisOffset, clipOffset = chart.clipOffset, directionFactor = [-1, 1, 1, -1][side], className = options.className;
  20536. let showAxis, titleOffset = 0, titleOffsetOption, titleMargin = 0, labelOffset = 0, // reset
  20537. labelOffsetPadded, lineHeightCorrection;
  20538. // For reuse in Axis.render
  20539. axis.showAxis = showAxis = hasData || options.showEmpty;
  20540. // Set/reset staggerLines
  20541. axis.staggerLines = (axis.horiz && labelOptions.staggerLines) || void 0;
  20542. // Create the axisGroup and gridGroup elements on first iteration
  20543. if (!axis.axisGroup) {
  20544. const createGroup = (name, suffix, zIndex) => renderer.g(name)
  20545. .attr({ zIndex })
  20546. .addClass(`highcharts-${coll.toLowerCase()}${suffix} ` +
  20547. (this.isRadial ? `highcharts-radial-axis${suffix} ` : '') +
  20548. (className || ''))
  20549. .add(axisParent);
  20550. axis.gridGroup = createGroup('grid', '-grid', options.gridZIndex);
  20551. axis.axisGroup = createGroup('axis', '', options.zIndex);
  20552. axis.labelGroup = createGroup('axis-labels', '-labels', labelOptions.zIndex);
  20553. }
  20554. if (hasData || axis.isLinked) {
  20555. // Generate ticks
  20556. tickPositions.forEach(function (pos) {
  20557. // i is not used here, but may be used in overrides
  20558. axis.generateTick(pos);
  20559. });
  20560. axis.renderUnsquish();
  20561. // Left side must be align: right and right side must
  20562. // have align: left for labels
  20563. axis.reserveSpaceDefault = (side === 0 ||
  20564. side === 2 ||
  20565. { 1: 'left', 3: 'right' }[side] === axis.labelAlign);
  20566. if (pick(labelOptions.reserveSpace, hasCrossing ? false : null, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
  20567. tickPositions.forEach(function (pos) {
  20568. // get the highest offset
  20569. labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
  20570. });
  20571. }
  20572. if (axis.staggerLines) {
  20573. labelOffset *= axis.staggerLines;
  20574. }
  20575. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  20576. }
  20577. else { // doesn't have data
  20578. objectEach(ticks, function (tick, n) {
  20579. tick.destroy();
  20580. delete ticks[n];
  20581. });
  20582. }
  20583. if (axisTitleOptions &&
  20584. axisTitleOptions.text &&
  20585. axisTitleOptions.enabled !== false) {
  20586. axis.addTitle(showAxis);
  20587. if (showAxis &&
  20588. !hasCrossing &&
  20589. axisTitleOptions.reserveSpace !== false) {
  20590. axis.titleOffset = titleOffset =
  20591. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  20592. titleOffsetOption = axisTitleOptions.offset;
  20593. titleMargin = defined(titleOffsetOption) ?
  20594. 0 :
  20595. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  20596. }
  20597. }
  20598. // Render the axis line
  20599. axis.renderLine();
  20600. // handle automatic or user set offset
  20601. axis.offset = directionFactor * pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
  20602. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  20603. if (side === 0) {
  20604. lineHeightCorrection = -axis.labelMetrics().h;
  20605. }
  20606. else if (side === 2) {
  20607. lineHeightCorrection = axis.tickRotCorr.y;
  20608. }
  20609. else {
  20610. lineHeightCorrection = 0;
  20611. }
  20612. // Find the padded label offset
  20613. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  20614. if (labelOffset) {
  20615. labelOffsetPadded -= lineHeightCorrection;
  20616. labelOffsetPadded += directionFactor * (horiz ?
  20617. pick(labelOptions.y, axis.tickRotCorr.y +
  20618. directionFactor * labelOptions.distance) :
  20619. pick(labelOptions.x, directionFactor * labelOptions.distance));
  20620. }
  20621. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  20622. if (axis.getMaxLabelDimensions) {
  20623. axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
  20624. }
  20625. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  20626. // has rendered.
  20627. if (coll !== 'colorAxis') {
  20628. const tickSize = this.tickSize('tick');
  20629. axisOffset[side] = Math.max(axisOffset[side], (axis.axisTitleMargin || 0) + titleOffset +
  20630. directionFactor * axis.offset, labelOffsetPadded, // #3027
  20631. tickPositions && tickPositions.length && tickSize ?
  20632. tickSize[0] + directionFactor * axis.offset :
  20633. 0 // #4866
  20634. );
  20635. // Decide the clipping needed to keep the graph inside
  20636. // the plot area and axis lines
  20637. const clip = !axis.axisLine || options.offset ?
  20638. 0 :
  20639. // #4308, #4371:
  20640. Math.floor(axis.axisLine.strokeWidth() / 2) * 2;
  20641. clipOffset[invertedSide] =
  20642. Math.max(clipOffset[invertedSide], clip);
  20643. }
  20644. fireEvent(this, 'afterGetOffset');
  20645. }
  20646. /**
  20647. * Internal function to get the path for the axis line. Extended for polar
  20648. * charts.
  20649. *
  20650. * @function Highcharts.Axis#getLinePath
  20651. *
  20652. * @param {number} lineWidth
  20653. * The line width in pixels.
  20654. *
  20655. * @return {Highcharts.SVGPathArray}
  20656. * The SVG path definition in array form.
  20657. */
  20658. getLinePath(lineWidth) {
  20659. const chart = this.chart, opposite = this.opposite, offset = this.offset, horiz = this.horiz, lineLeft = this.left + (opposite ? this.width : 0) + offset, lineTop = chart.chartHeight - this.bottom -
  20660. (opposite ? this.height : 0) + offset;
  20661. if (opposite) {
  20662. lineWidth *= -1; // crispify the other way - #1480, #1687
  20663. }
  20664. return chart.renderer
  20665. .crispLine([
  20666. [
  20667. 'M',
  20668. horiz ?
  20669. this.left :
  20670. lineLeft,
  20671. horiz ?
  20672. lineTop :
  20673. this.top
  20674. ],
  20675. [
  20676. 'L',
  20677. horiz ?
  20678. chart.chartWidth - this.right :
  20679. lineLeft,
  20680. horiz ?
  20681. lineTop :
  20682. chart.chartHeight - this.bottom
  20683. ]
  20684. ], lineWidth);
  20685. }
  20686. /**
  20687. * Render the axis line. Called internally when rendering and redrawing the
  20688. * axis.
  20689. *
  20690. * @function Highcharts.Axis#renderLine
  20691. */
  20692. renderLine() {
  20693. if (!this.axisLine) {
  20694. this.axisLine = this.chart.renderer.path()
  20695. .addClass('highcharts-axis-line')
  20696. .add(this.axisGroup);
  20697. if (!this.chart.styledMode) {
  20698. this.axisLine.attr({
  20699. stroke: this.options.lineColor,
  20700. 'stroke-width': this.options.lineWidth,
  20701. zIndex: 7
  20702. });
  20703. }
  20704. }
  20705. }
  20706. /**
  20707. * Position the axis title.
  20708. *
  20709. * @private
  20710. * @function Highcharts.Axis#getTitlePosition
  20711. *
  20712. * @return {Highcharts.PositionObject}
  20713. * X and Y positions for the title.
  20714. */
  20715. getTitlePosition(axisTitle) {
  20716. // compute anchor points for each of the title align options
  20717. const horiz = this.horiz, axisLeft = this.left, axisTop = this.top, axisLength = this.len, axisTitleOptions = this.options.title, margin = horiz ? axisLeft : axisTop, opposite = this.opposite, offset = this.offset, xOption = axisTitleOptions.x, yOption = axisTitleOptions.y, fontMetrics = this.chart.renderer.fontMetrics(axisTitle),
  20718. // The part of a multiline text that is below the baseline of the
  20719. // first line. Subtract 1 to preserve pixel-perfectness from the
  20720. // old behaviour (v5.0.12), where only one line was allowed.
  20721. textHeightOvershoot = axisTitle ? Math.max(axisTitle.getBBox(false, 0).height - fontMetrics.h - 1, 0) : 0,
  20722. // the position in the length direction of the axis
  20723. alongAxis = ({
  20724. low: margin + (horiz ? 0 : axisLength),
  20725. middle: margin + axisLength / 2,
  20726. high: margin + (horiz ? axisLength : 0)
  20727. })[axisTitleOptions.align],
  20728. // the position in the perpendicular direction of the axis
  20729. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  20730. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  20731. (opposite ? -1 : 1) * // so does opposite axes
  20732. (this.axisTitleMargin || 0) +
  20733. [
  20734. -textHeightOvershoot,
  20735. textHeightOvershoot,
  20736. fontMetrics.f,
  20737. -textHeightOvershoot // left
  20738. ][this.side], titlePosition = {
  20739. x: horiz ?
  20740. alongAxis + xOption :
  20741. offAxis + (opposite ? this.width : 0) + offset + xOption,
  20742. y: horiz ?
  20743. offAxis + yOption - (opposite ? this.height : 0) + offset :
  20744. alongAxis + yOption
  20745. };
  20746. fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
  20747. return titlePosition;
  20748. }
  20749. /**
  20750. * Render a minor tick into the given position. If a minor tick already
  20751. * exists in this position, move it.
  20752. *
  20753. * @function Highcharts.Axis#renderMinorTick
  20754. *
  20755. * @param {number} pos
  20756. * The position in axis values.
  20757. *
  20758. * @param {boolean} slideIn
  20759. * Whether the tick should animate in from last computed position
  20760. */
  20761. renderMinorTick(pos, slideIn) {
  20762. const axis = this;
  20763. const minorTicks = axis.minorTicks;
  20764. if (!minorTicks[pos]) {
  20765. minorTicks[pos] = new Tick(axis, pos, 'minor');
  20766. }
  20767. // Render new ticks in old position
  20768. if (slideIn && minorTicks[pos].isNew) {
  20769. minorTicks[pos].render(null, true);
  20770. }
  20771. minorTicks[pos].render(null, false, 1);
  20772. }
  20773. /**
  20774. * Render a major tick into the given position. If a tick already exists
  20775. * in this position, move it.
  20776. *
  20777. * @function Highcharts.Axis#renderTick
  20778. *
  20779. * @param {number} pos
  20780. * The position in axis values.
  20781. *
  20782. * @param {number} i
  20783. * The tick index.
  20784. *
  20785. * @param {boolean} slideIn
  20786. * Whether the tick should animate in from last computed position
  20787. */
  20788. renderTick(pos, i, slideIn) {
  20789. const axis = this, isLinked = axis.isLinked, ticks = axis.ticks;
  20790. // Linked axes need an extra check to find out if
  20791. if (!isLinked ||
  20792. (pos >= axis.min && pos <= axis.max) ||
  20793. (axis.grid && axis.grid.isColumn)) {
  20794. if (!ticks[pos]) {
  20795. ticks[pos] = new Tick(axis, pos);
  20796. }
  20797. // NOTE this seems like overkill. Could be handled in tick.render by
  20798. // setting old position in attr, then set new position in animate.
  20799. // render new ticks in old position
  20800. if (slideIn && ticks[pos].isNew) {
  20801. // Start with negative opacity so that it is visible from
  20802. // halfway into the animation
  20803. ticks[pos].render(i, true, -1);
  20804. }
  20805. ticks[pos].render(i);
  20806. }
  20807. }
  20808. /**
  20809. * Render the axis.
  20810. *
  20811. * @private
  20812. * @function Highcharts.Axis#render
  20813. *
  20814. * @emits Highcharts.Axis#event:afterRender
  20815. */
  20816. render() {
  20817. const axis = this, chart = axis.chart, log = axis.logarithmic, renderer = chart.renderer, options = axis.options, isLinked = axis.isLinked, tickPositions = axis.tickPositions, axisTitle = axis.axisTitle, ticks = axis.ticks, minorTicks = axis.minorTicks, alternateBands = axis.alternateBands, stackLabelOptions = options.stackLabels, alternateGridColor = options.alternateGridColor, crossing = options.crossing, tickmarkOffset = axis.tickmarkOffset, axisLine = axis.axisLine, showAxis = axis.showAxis, animation = animObject(renderer.globalAnimation);
  20818. let from, to;
  20819. // Reset
  20820. axis.labelEdge.length = 0;
  20821. axis.overlap = false;
  20822. // Mark all elements inActive before we go over and mark the active ones
  20823. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  20824. objectEach(coll, function (tick) {
  20825. tick.isActive = false;
  20826. });
  20827. });
  20828. // Crossing
  20829. if (isNumber(crossing)) {
  20830. const otherAxis = this.isXAxis ? chart.yAxis[0] : chart.xAxis[0], directionFactor = [1, -1, -1, 1][this.side];
  20831. if (otherAxis) {
  20832. let px = otherAxis.toPixels(crossing, true);
  20833. if (axis.horiz) {
  20834. px = otherAxis.len - px;
  20835. }
  20836. axis.offset = directionFactor * px;
  20837. }
  20838. }
  20839. // If the series has data draw the ticks. Else only the line and title
  20840. if (axis.hasData() || isLinked) {
  20841. const slideInTicks = axis.chart.hasRendered &&
  20842. axis.old && isNumber(axis.old.min);
  20843. // minor ticks
  20844. if (axis.minorTickInterval && !axis.categories) {
  20845. axis.getMinorTickPositions().forEach(function (pos) {
  20846. axis.renderMinorTick(pos, slideInTicks);
  20847. });
  20848. }
  20849. // Major ticks. Pull out the first item and render it last so that
  20850. // we can get the position of the neighbour label. #808.
  20851. if (tickPositions.length) { // #1300
  20852. tickPositions.forEach(function (pos, i) {
  20853. axis.renderTick(pos, i, slideInTicks);
  20854. });
  20855. // In a categorized axis, the tick marks are displayed
  20856. // between labels. So we need to add a tick mark and
  20857. // grid line at the left edge of the X axis.
  20858. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  20859. if (!ticks[-1]) {
  20860. ticks[-1] = new Tick(axis, -1, null, true);
  20861. }
  20862. ticks[-1].render(-1);
  20863. }
  20864. }
  20865. // alternate grid color
  20866. if (alternateGridColor) {
  20867. tickPositions.forEach(function (pos, i) {
  20868. to = typeof tickPositions[i + 1] !== 'undefined' ?
  20869. tickPositions[i + 1] + tickmarkOffset :
  20870. axis.max - tickmarkOffset;
  20871. if (i % 2 === 0 &&
  20872. pos < axis.max &&
  20873. to <= axis.max + (chart.polar ?
  20874. -tickmarkOffset :
  20875. tickmarkOffset)) { // #2248, #4660
  20876. if (!alternateBands[pos]) {
  20877. // Should be imported from PlotLineOrBand.js, but
  20878. // the dependency cycle with axis is a problem
  20879. alternateBands[pos] = new H.PlotLineOrBand(axis);
  20880. }
  20881. from = pos + tickmarkOffset; // #949
  20882. alternateBands[pos].options = {
  20883. from: log ? log.lin2log(from) : from,
  20884. to: log ? log.lin2log(to) : to,
  20885. color: alternateGridColor,
  20886. className: 'highcharts-alternate-grid'
  20887. };
  20888. alternateBands[pos].render();
  20889. alternateBands[pos].isActive = true;
  20890. }
  20891. });
  20892. }
  20893. // custom plot lines and bands
  20894. if (!axis._addedPlotLB) { // only first time
  20895. axis._addedPlotLB = true;
  20896. (options.plotLines || [])
  20897. .concat(options.plotBands || [])
  20898. .forEach(function (plotLineOptions) {
  20899. axis
  20900. .addPlotBandOrLine(plotLineOptions);
  20901. });
  20902. }
  20903. } // end if hasData
  20904. // Remove inactive ticks
  20905. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  20906. const forDestruction = [], delay = animation.duration, destroyInactiveItems = function () {
  20907. let i = forDestruction.length;
  20908. while (i--) {
  20909. // When resizing rapidly, the same items
  20910. // may be destroyed in different timeouts,
  20911. // or the may be reactivated
  20912. if (coll[forDestruction[i]] &&
  20913. !coll[forDestruction[i]].isActive) {
  20914. coll[forDestruction[i]].destroy();
  20915. delete coll[forDestruction[i]];
  20916. }
  20917. }
  20918. };
  20919. objectEach(coll, function (tick, pos) {
  20920. if (!tick.isActive) {
  20921. // Render to zero opacity
  20922. tick.render(pos, false, 0);
  20923. tick.isActive = false;
  20924. forDestruction.push(pos);
  20925. }
  20926. });
  20927. // When the objects are finished fading out, destroy them
  20928. syncTimeout(destroyInactiveItems, coll === alternateBands ||
  20929. !chart.hasRendered ||
  20930. !delay ?
  20931. 0 :
  20932. delay);
  20933. });
  20934. // Set the axis line path
  20935. if (axisLine) {
  20936. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  20937. d: this.getLinePath(axisLine.strokeWidth())
  20938. });
  20939. axisLine.isPlaced = true;
  20940. // Show or hide the line depending on options.showEmpty
  20941. axisLine[showAxis ? 'show' : 'hide'](showAxis);
  20942. }
  20943. if (axisTitle && showAxis) {
  20944. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](axis.getTitlePosition(axisTitle));
  20945. axisTitle.isNew = false;
  20946. }
  20947. // Stacked totals:
  20948. if (stackLabelOptions && stackLabelOptions.enabled && axis.stacking) {
  20949. axis.stacking.renderStackTotals();
  20950. }
  20951. // End stacked totals
  20952. // Record old scaling for updating/animation
  20953. axis.old = {
  20954. len: axis.len,
  20955. max: axis.max,
  20956. min: axis.min,
  20957. transA: axis.transA,
  20958. userMax: axis.userMax,
  20959. userMin: axis.userMin
  20960. };
  20961. axis.isDirty = false;
  20962. fireEvent(this, 'afterRender');
  20963. }
  20964. /**
  20965. * Redraw the axis to reflect changes in the data or axis extremes. Called
  20966. * internally from Highcharts.Chart#redraw.
  20967. *
  20968. * @private
  20969. * @function Highcharts.Axis#redraw
  20970. */
  20971. redraw() {
  20972. if (this.visible) {
  20973. // render the axis
  20974. this.render();
  20975. // move plot lines and bands
  20976. this.plotLinesAndBands.forEach(function (plotLine) {
  20977. plotLine.render();
  20978. });
  20979. }
  20980. // mark associated series as dirty and ready for redraw
  20981. this.series.forEach(function (series) {
  20982. series.isDirty = true;
  20983. });
  20984. }
  20985. /**
  20986. * Returns an array of axis properties, that should be untouched during
  20987. * reinitialization.
  20988. *
  20989. * @private
  20990. * @function Highcharts.Axis#getKeepProps
  20991. */
  20992. getKeepProps() {
  20993. return (this.keepProps || Axis.keepProps);
  20994. }
  20995. /**
  20996. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  20997. * to fully remove the axis.
  20998. *
  20999. * @private
  21000. * @function Highcharts.Axis#destroy
  21001. *
  21002. * @param {boolean} [keepEvents]
  21003. * Whether to preserve events, used internally in Axis.update.
  21004. */
  21005. destroy(keepEvents) {
  21006. const axis = this, plotLinesAndBands = axis.plotLinesAndBands, eventOptions = this.eventOptions;
  21007. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  21008. // Remove the events
  21009. if (!keepEvents) {
  21010. removeEvent(axis);
  21011. }
  21012. // Destroy collections
  21013. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
  21014. destroyObjectProperties(coll);
  21015. });
  21016. if (plotLinesAndBands) {
  21017. let i = plotLinesAndBands.length;
  21018. while (i--) { // #1975
  21019. plotLinesAndBands[i].destroy();
  21020. }
  21021. }
  21022. // Destroy elements
  21023. ['axisLine', 'axisTitle', 'axisGroup',
  21024. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(function (prop) {
  21025. if (axis[prop]) {
  21026. axis[prop] = axis[prop].destroy();
  21027. }
  21028. });
  21029. // Destroy each generated group for plotlines and plotbands
  21030. for (const plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
  21031. axis.plotLinesAndBandsGroups[plotGroup] =
  21032. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  21033. }
  21034. // Delete all properties and fall back to the prototype.
  21035. objectEach(axis, function (val, key) {
  21036. if (axis.getKeepProps().indexOf(key) === -1) {
  21037. delete axis[key];
  21038. }
  21039. });
  21040. this.eventOptions = eventOptions;
  21041. }
  21042. /**
  21043. * Internal function to draw a crosshair.
  21044. *
  21045. * @function Highcharts.Axis#drawCrosshair
  21046. *
  21047. * @param {Highcharts.PointerEventObject} [e]
  21048. * The event arguments from the modified pointer event, extended with
  21049. * `chartX` and `chartY`
  21050. *
  21051. * @param {Highcharts.Point} [point]
  21052. * The Point object if the crosshair snaps to points.
  21053. *
  21054. * @emits Highcharts.Axis#event:afterDrawCrosshair
  21055. * @emits Highcharts.Axis#event:drawCrosshair
  21056. */
  21057. drawCrosshair(e, point) {
  21058. const options = this.crosshair, snap = pick(options && options.snap, true), chart = this.chart;
  21059. let path, pos, categorized, graphic = this.cross, crossOptions;
  21060. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  21061. // Use last available event when updating non-snapped crosshairs without
  21062. // mouse interaction (#5287)
  21063. if (!e) {
  21064. e = this.cross && this.cross.e;
  21065. }
  21066. if (
  21067. // Disabled in options
  21068. !options ||
  21069. // Snap
  21070. ((defined(point) || !snap) === false)) {
  21071. this.hideCrosshair();
  21072. }
  21073. else {
  21074. // Get the path
  21075. if (!snap) {
  21076. pos = e &&
  21077. (this.horiz ?
  21078. e.chartX - this.pos :
  21079. this.len - e.chartY + this.pos);
  21080. }
  21081. else if (defined(point)) {
  21082. // #3834
  21083. pos = pick(this.coll !== 'colorAxis' ?
  21084. point.crosshairPos : // 3D axis extension
  21085. null, this.isXAxis ?
  21086. point.plotX :
  21087. this.len - point.plotY);
  21088. }
  21089. if (defined(pos)) {
  21090. crossOptions = {
  21091. // value, only used on radial
  21092. value: point && (this.isXAxis ?
  21093. point.x :
  21094. pick(point.stackY, point.y)),
  21095. translatedValue: pos
  21096. };
  21097. if (chart.polar) {
  21098. // Additional information required for crosshairs in
  21099. // polar chart
  21100. extend(crossOptions, {
  21101. isCrosshair: true,
  21102. chartX: e && e.chartX,
  21103. chartY: e && e.chartY,
  21104. point: point
  21105. });
  21106. }
  21107. path = this.getPlotLinePath(crossOptions) ||
  21108. null; // #3189
  21109. }
  21110. if (!defined(path)) {
  21111. this.hideCrosshair();
  21112. return;
  21113. }
  21114. categorized = this.categories && !this.isRadial;
  21115. // Draw the cross
  21116. if (!graphic) {
  21117. this.cross = graphic = chart.renderer
  21118. .path()
  21119. .addClass('highcharts-crosshair highcharts-crosshair-' +
  21120. (categorized ? 'category ' : 'thin ') +
  21121. (options.className || ''))
  21122. .attr({
  21123. zIndex: pick(options.zIndex, 2)
  21124. })
  21125. .add();
  21126. // Presentational attributes
  21127. if (!chart.styledMode) {
  21128. graphic.attr({
  21129. stroke: options.color ||
  21130. (categorized ?
  21131. Color
  21132. .parse("#ccd3ff" /* Palette.highlightColor20 */)
  21133. .setOpacity(0.25)
  21134. .get() :
  21135. "#cccccc" /* Palette.neutralColor20 */),
  21136. 'stroke-width': pick(options.width, 1)
  21137. }).css({
  21138. 'pointer-events': 'none'
  21139. });
  21140. if (options.dashStyle) {
  21141. graphic.attr({
  21142. dashstyle: options.dashStyle
  21143. });
  21144. }
  21145. }
  21146. }
  21147. graphic.show().attr({
  21148. d: path
  21149. });
  21150. if (categorized && !options.width) {
  21151. graphic.attr({
  21152. 'stroke-width': this.transA
  21153. });
  21154. }
  21155. this.cross.e = e;
  21156. }
  21157. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  21158. }
  21159. /**
  21160. * Hide the crosshair if visible.
  21161. *
  21162. * @function Highcharts.Axis#hideCrosshair
  21163. */
  21164. hideCrosshair() {
  21165. if (this.cross) {
  21166. this.cross.hide();
  21167. }
  21168. fireEvent(this, 'afterHideCrosshair');
  21169. }
  21170. /**
  21171. * Check whether the chart has vertical panning ('y' or 'xy' type).
  21172. *
  21173. * @private
  21174. * @function Highcharts.Axis#hasVerticalPanning
  21175. */
  21176. hasVerticalPanning() {
  21177. const panningOptions = this.chart.options.chart.panning;
  21178. return Boolean(panningOptions &&
  21179. panningOptions.enabled && // #14624
  21180. /y/.test(panningOptions.type));
  21181. }
  21182. /**
  21183. * Update an axis object with a new set of options. The options are merged
  21184. * with the existing options, so only new or altered options need to be
  21185. * specified.
  21186. *
  21187. * @sample highcharts/members/axis-update/
  21188. * Axis update demo
  21189. *
  21190. * @function Highcharts.Axis#update
  21191. *
  21192. * @param {Highcharts.AxisOptions} options
  21193. * The new options that will be merged in with existing options on the axis.
  21194. *
  21195. * @param {boolean} [redraw=true]
  21196. * Whether to redraw the chart after the axis is altered. If doing more
  21197. * operations on the chart, it is a good idea to set redraw to false and
  21198. * call {@link Chart#redraw} after.
  21199. */
  21200. update(options, redraw) {
  21201. const chart = this.chart;
  21202. options = merge(this.userOptions, options);
  21203. this.destroy(true);
  21204. this.init(chart, options);
  21205. chart.isDirtyBox = true;
  21206. if (pick(redraw, true)) {
  21207. chart.redraw();
  21208. }
  21209. }
  21210. /**
  21211. * Remove the axis from the chart.
  21212. *
  21213. * @sample highcharts/members/chart-addaxis/
  21214. * Add and remove axes
  21215. *
  21216. * @function Highcharts.Axis#remove
  21217. *
  21218. * @param {boolean} [redraw=true]
  21219. * Whether to redraw the chart following the remove.
  21220. */
  21221. remove(redraw) {
  21222. const chart = this.chart, coll = this.coll, axisSeries = this.series;
  21223. let i = axisSeries.length;
  21224. // Remove associated series (#2687)
  21225. while (i--) {
  21226. if (axisSeries[i]) {
  21227. axisSeries[i].remove(false);
  21228. }
  21229. }
  21230. // Remove the axis
  21231. erase(chart.axes, this);
  21232. erase(chart[coll] || [], this);
  21233. chart.orderItems(coll);
  21234. this.destroy();
  21235. chart.isDirtyBox = true;
  21236. if (pick(redraw, true)) {
  21237. chart.redraw();
  21238. }
  21239. }
  21240. /**
  21241. * Update the axis title by options after render time.
  21242. *
  21243. * @sample highcharts/members/axis-settitle/
  21244. * Set a new Y axis title
  21245. *
  21246. * @function Highcharts.Axis#setTitle
  21247. *
  21248. * @param {Highcharts.AxisTitleOptions} titleOptions
  21249. * The additional title options.
  21250. *
  21251. * @param {boolean} [redraw=true]
  21252. * Whether to redraw the chart after setting the title.
  21253. */
  21254. setTitle(titleOptions, redraw) {
  21255. this.update({ title: titleOptions }, redraw);
  21256. }
  21257. /**
  21258. * Set new axis categories and optionally redraw.
  21259. *
  21260. * @sample highcharts/members/axis-setcategories/
  21261. * Set categories by click on a button
  21262. *
  21263. * @function Highcharts.Axis#setCategories
  21264. *
  21265. * @param {Array<string>} categories
  21266. * The new categories.
  21267. *
  21268. * @param {boolean} [redraw=true]
  21269. * Whether to redraw the chart.
  21270. */
  21271. setCategories(categories, redraw) {
  21272. this.update({ categories: categories }, redraw);
  21273. }
  21274. }
  21275. /* *
  21276. *
  21277. * Static Properties
  21278. *
  21279. * */
  21280. Axis.defaultOptions = AxisDefaults.defaultXAxisOptions;
  21281. // Properties to survive after destroy, needed for Axis.update (#4317,
  21282. // #5773, #5881).
  21283. Axis.keepProps = [
  21284. 'coll',
  21285. 'extKey',
  21286. 'hcEvents',
  21287. 'names',
  21288. 'series',
  21289. 'userMax',
  21290. 'userMin'
  21291. ];
  21292. /* *
  21293. *
  21294. * Default Export
  21295. *
  21296. * */
  21297. /* *
  21298. *
  21299. * API Declarations
  21300. *
  21301. * */
  21302. /**
  21303. * Options for the path on the Axis to be calculated.
  21304. * @interface Highcharts.AxisPlotLinePathOptionsObject
  21305. */ /**
  21306. * Axis value.
  21307. * @name Highcharts.AxisPlotLinePathOptionsObject#value
  21308. * @type {number|undefined}
  21309. */ /**
  21310. * Line width used for calculation crisp line coordinates. Defaults to 1.
  21311. * @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
  21312. * @type {number|undefined}
  21313. */ /**
  21314. * If `false`, the function will return null when it falls outside the axis
  21315. * bounds. If `true`, the function will return a path aligned to the plot area
  21316. * sides if it falls outside. If `pass`, it will return a path outside.
  21317. * @name Highcharts.AxisPlotLinePathOptionsObject#force
  21318. * @type {string|boolean|undefined}
  21319. */ /**
  21320. * Used in Highcharts Stock. When `true`, plot paths
  21321. * (crosshair, plotLines, gridLines)
  21322. * will be rendered on all axes when defined on the first axis.
  21323. * @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
  21324. * @type {boolean|undefined}
  21325. */ /**
  21326. * Use old coordinates (for resizing and rescaling).
  21327. * If not set, defaults to `false`.
  21328. * @name Highcharts.AxisPlotLinePathOptionsObject#old
  21329. * @type {boolean|undefined}
  21330. */ /**
  21331. * If given, return the plot line path of a pixel position on the axis.
  21332. * @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
  21333. * @type {number|undefined}
  21334. */ /**
  21335. * Used in Polar axes. Reverse the positions for concatenation of polygonal
  21336. * plot bands
  21337. * @name Highcharts.AxisPlotLinePathOptionsObject#reverse
  21338. * @type {boolean|undefined}
  21339. */
  21340. /**
  21341. * Options for crosshairs on axes.
  21342. *
  21343. * @product highstock
  21344. *
  21345. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  21346. */
  21347. /**
  21348. * @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
  21349. */
  21350. /**
  21351. * @callback Highcharts.AxisEventCallbackFunction
  21352. *
  21353. * @param {Highcharts.Axis} this
  21354. */
  21355. /**
  21356. * @callback Highcharts.AxisLabelsFormatterCallbackFunction
  21357. *
  21358. * @param {Highcharts.AxisLabelsFormatterContextObject} this
  21359. *
  21360. * @param {Highcharts.AxisLabelsFormatterContextObject} ctx
  21361. *
  21362. * @return {string}
  21363. */
  21364. /**
  21365. * @interface Highcharts.AxisLabelsFormatterContextObject
  21366. */ /**
  21367. * The axis item of the label
  21368. * @name Highcharts.AxisLabelsFormatterContextObject#axis
  21369. * @type {Highcharts.Axis}
  21370. */ /**
  21371. * The chart instance.
  21372. * @name Highcharts.AxisLabelsFormatterContextObject#chart
  21373. * @type {Highcharts.Chart}
  21374. */ /**
  21375. * Default formatting of date/time labels.
  21376. * @name Highcharts.AxisLabelsFormatterContextObject#dateTimeLabelFormat
  21377. * @type {string|undefined}
  21378. */ /**
  21379. * Whether the label belongs to the first tick on the axis.
  21380. * @name Highcharts.AxisLabelsFormatterContextObject#isFirst
  21381. * @type {boolean}
  21382. */ /**
  21383. * Whether the label belongs to the last tick on the axis.
  21384. * @name Highcharts.AxisLabelsFormatterContextObject#isLast
  21385. * @type {boolean}
  21386. */ /**
  21387. * The position on the axis in terms of axis values. For category axes, a
  21388. * zero-based index. For datetime axes, the JavaScript time in milliseconds
  21389. * since 1970.
  21390. * @name Highcharts.AxisLabelsFormatterContextObject#pos
  21391. * @type {number}
  21392. */ /**
  21393. * The preformatted text as the result of the default formatting. For example
  21394. * dates will be formatted as strings, and numbers with language-specific comma
  21395. * separators, thousands separators and numeric symbols like `k` or `M`.
  21396. * @name Highcharts.AxisLabelsFormatterContextObject#text
  21397. * @type {string|undefined}
  21398. */ /**
  21399. * The Tick instance.
  21400. * @name Highcharts.AxisLabelsFormatterContextObject#tick
  21401. * @type {Highcharts.Tick}
  21402. */ /**
  21403. * This can be either a numeric value or a category string.
  21404. * @name Highcharts.AxisLabelsFormatterContextObject#value
  21405. * @type {number|string}
  21406. */
  21407. /**
  21408. * Options for axes.
  21409. *
  21410. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  21411. */
  21412. /**
  21413. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  21414. *
  21415. * @param {Highcharts.Axis} this
  21416. *
  21417. * @param {Highcharts.AxisPointBreakEventObject} evt
  21418. */
  21419. /**
  21420. * @interface Highcharts.AxisPointBreakEventObject
  21421. */ /**
  21422. * @name Highcharts.AxisPointBreakEventObject#brk
  21423. * @type {Highcharts.Dictionary<number>}
  21424. */ /**
  21425. * @name Highcharts.AxisPointBreakEventObject#point
  21426. * @type {Highcharts.Point}
  21427. */ /**
  21428. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  21429. * @type {Function}
  21430. */ /**
  21431. * @name Highcharts.AxisPointBreakEventObject#target
  21432. * @type {Highcharts.SVGElement}
  21433. */ /**
  21434. * @name Highcharts.AxisPointBreakEventObject#type
  21435. * @type {"pointBreak"|"pointInBreak"}
  21436. */
  21437. /**
  21438. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  21439. *
  21440. * @param {Highcharts.Axis} this
  21441. *
  21442. * @param {Highcharts.AxisSetExtremesEventObject} evt
  21443. */
  21444. /**
  21445. * @interface Highcharts.AxisSetExtremesEventObject
  21446. * @extends Highcharts.ExtremesObject
  21447. */ /**
  21448. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  21449. * @type {Function}
  21450. */ /**
  21451. * @name Highcharts.AxisSetExtremesEventObject#target
  21452. * @type {Highcharts.SVGElement}
  21453. */ /**
  21454. * @name Highcharts.AxisSetExtremesEventObject#trigger
  21455. * @type {Highcharts.AxisExtremesTriggerValue|string}
  21456. */ /**
  21457. * @name Highcharts.AxisSetExtremesEventObject#type
  21458. * @type {"setExtremes"}
  21459. */
  21460. /**
  21461. * @callback Highcharts.AxisTickPositionerCallbackFunction
  21462. *
  21463. * @param {Highcharts.Axis} this
  21464. *
  21465. * @return {Highcharts.AxisTickPositionsArray}
  21466. */
  21467. /**
  21468. * @interface Highcharts.AxisTickPositionsArray
  21469. * @augments Array<number>
  21470. */
  21471. /**
  21472. * @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
  21473. */
  21474. /**
  21475. * @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
  21476. */
  21477. /**
  21478. * @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
  21479. */
  21480. /**
  21481. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  21482. * function.
  21483. *
  21484. * @interface Highcharts.ExtremesObject
  21485. */ /**
  21486. * The maximum value of the axis' associated series.
  21487. * @name Highcharts.ExtremesObject#dataMax
  21488. * @type {number}
  21489. */ /**
  21490. * The minimum value of the axis' associated series.
  21491. * @name Highcharts.ExtremesObject#dataMin
  21492. * @type {number}
  21493. */ /**
  21494. * The maximum axis value, either automatic or set manually. If the `max` option
  21495. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  21496. * the same as `dataMax`.
  21497. * @name Highcharts.ExtremesObject#max
  21498. * @type {number}
  21499. */ /**
  21500. * The minimum axis value, either automatic or set manually. If the `min` option
  21501. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  21502. * the same as `dataMin`.
  21503. * @name Highcharts.ExtremesObject#min
  21504. * @type {number}
  21505. */ /**
  21506. * The user defined maximum, either from the `max` option or from a zoom or
  21507. * `setExtremes` action.
  21508. * @name Highcharts.ExtremesObject#userMax
  21509. * @type {number|undefined}
  21510. */ /**
  21511. * The user defined minimum, either from the `min` option or from a zoom or
  21512. * `setExtremes` action.
  21513. * @name Highcharts.ExtremesObject#userMin
  21514. * @type {number|undefined}
  21515. */
  21516. /**
  21517. * Formatter function for the text of a crosshair label.
  21518. *
  21519. * @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
  21520. *
  21521. * @param {Highcharts.Axis} this
  21522. * Axis context
  21523. *
  21524. * @param {number} value
  21525. * Y value of the data point
  21526. *
  21527. * @return {string}
  21528. */
  21529. ''; // keeps doclets above in JS file
  21530. return Axis;
  21531. });
  21532. _registerModule(_modules, 'Core/Axis/DateTimeAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21533. /* *
  21534. *
  21535. * (c) 2010-2021 Torstein Honsi
  21536. *
  21537. * License: www.highcharts.com/license
  21538. *
  21539. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21540. *
  21541. * */
  21542. const { addEvent, getMagnitude, normalizeTickInterval, timeUnits } = U;
  21543. /* *
  21544. *
  21545. * Composition
  21546. *
  21547. * */
  21548. /* eslint-disable valid-jsdoc */
  21549. var DateTimeAxis;
  21550. (function (DateTimeAxis) {
  21551. /* *
  21552. *
  21553. * Declarations
  21554. *
  21555. * */
  21556. /* *
  21557. *
  21558. * Constants
  21559. *
  21560. * */
  21561. const composedMembers = [];
  21562. /* *
  21563. *
  21564. * Functions
  21565. *
  21566. * */
  21567. /**
  21568. * Extends axis class with date and time support.
  21569. * @private
  21570. */
  21571. function compose(AxisClass) {
  21572. if (U.pushUnique(composedMembers, AxisClass)) {
  21573. AxisClass.keepProps.push('dateTime');
  21574. const axisProto = AxisClass.prototype;
  21575. axisProto.getTimeTicks = getTimeTicks;
  21576. addEvent(AxisClass, 'init', onInit);
  21577. }
  21578. return AxisClass;
  21579. }
  21580. DateTimeAxis.compose = compose;
  21581. /**
  21582. * Set the tick positions to a time unit that makes sense, for example
  21583. * on the first of each month or on every Monday. Return an array with
  21584. * the time positions. Used in datetime axes as well as for grouping
  21585. * data on a datetime axis.
  21586. *
  21587. * @private
  21588. * @function Highcharts.Axis#getTimeTicks
  21589. * @param {Highcharts.TimeNormalizeObject} normalizedInterval
  21590. * The interval in axis values (ms) and thecount.
  21591. * @param {number} min
  21592. * The minimum in axis values.
  21593. * @param {number} max
  21594. * The maximum in axis values.
  21595. */
  21596. function getTimeTicks() {
  21597. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  21598. }
  21599. /**
  21600. * @private
  21601. */
  21602. function onInit(e) {
  21603. const axis = this;
  21604. const options = e.userOptions;
  21605. if (options.type !== 'datetime') {
  21606. axis.dateTime = void 0;
  21607. return;
  21608. }
  21609. if (!axis.dateTime) {
  21610. axis.dateTime = new Additions(axis);
  21611. }
  21612. }
  21613. /* *
  21614. *
  21615. * Classes
  21616. *
  21617. * */
  21618. class Additions {
  21619. /* *
  21620. *
  21621. * Constructors
  21622. *
  21623. * */
  21624. constructor(axis) {
  21625. this.axis = axis;
  21626. }
  21627. /* *
  21628. *
  21629. * Functions
  21630. *
  21631. * */
  21632. /**
  21633. * Get a normalized tick interval for dates. Returns a configuration
  21634. * object with unit range (interval), count and name. Used to prepare
  21635. * data for `getTimeTicks`. Previously this logic was part of
  21636. * getTimeTicks, but as `getTimeTicks` now runs of segments in stock
  21637. * charts, the normalizing logic was extracted in order to prevent it
  21638. * for running over again for each segment having the same interval.
  21639. * #662, #697.
  21640. * @private
  21641. */
  21642. normalizeTimeTickInterval(tickInterval, unitsOption) {
  21643. const units = (unitsOption || [[
  21644. // unit name
  21645. 'millisecond',
  21646. // allowed multiples
  21647. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]
  21648. ], [
  21649. 'second',
  21650. [1, 2, 5, 10, 15, 30]
  21651. ], [
  21652. 'minute',
  21653. [1, 2, 5, 10, 15, 30]
  21654. ], [
  21655. 'hour',
  21656. [1, 2, 3, 4, 6, 8, 12]
  21657. ], [
  21658. 'day',
  21659. [1, 2]
  21660. ], [
  21661. 'week',
  21662. [1, 2]
  21663. ], [
  21664. 'month',
  21665. [1, 2, 3, 4, 6]
  21666. ], [
  21667. 'year',
  21668. null
  21669. ]]);
  21670. let unit = units[units.length - 1], // default unit is years
  21671. interval = timeUnits[unit[0]], multiples = unit[1], i;
  21672. // loop through the units to find the one that best fits the
  21673. // tickInterval
  21674. for (i = 0; i < units.length; i++) {
  21675. unit = units[i];
  21676. interval = timeUnits[unit[0]];
  21677. multiples = unit[1];
  21678. if (units[i + 1]) {
  21679. // lessThan is in the middle between the highest multiple
  21680. // and the next unit.
  21681. const lessThan = (interval *
  21682. multiples[multiples.length - 1] +
  21683. timeUnits[units[i + 1][0]]) / 2;
  21684. // break and keep the current unit
  21685. if (tickInterval <= lessThan) {
  21686. break;
  21687. }
  21688. }
  21689. }
  21690. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  21691. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  21692. multiples = [1, 2, 5];
  21693. }
  21694. // get the count
  21695. const count = normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ? // #1913, #2360
  21696. Math.max(getMagnitude(tickInterval / interval), 1) :
  21697. 1);
  21698. return {
  21699. unitRange: interval,
  21700. count: count,
  21701. unitName: unit[0]
  21702. };
  21703. }
  21704. /**
  21705. * Get the best date format for a specific X value based on the closest
  21706. * point range on the axis.
  21707. *
  21708. * @private
  21709. */
  21710. getXDateFormat(x, dateTimeLabelFormats) {
  21711. const { axis } = this, time = axis.chart.time;
  21712. return axis.closestPointRange ?
  21713. time.getDateFormat(axis.closestPointRange, x, axis.options.startOfWeek, dateTimeLabelFormats) ||
  21714. // #2546, 2581
  21715. time.resolveDTLFormat(dateTimeLabelFormats.year).main :
  21716. time.resolveDTLFormat(dateTimeLabelFormats.day).main;
  21717. }
  21718. }
  21719. DateTimeAxis.Additions = Additions;
  21720. })(DateTimeAxis || (DateTimeAxis = {}));
  21721. /* *
  21722. *
  21723. * Default Export
  21724. *
  21725. * */
  21726. return DateTimeAxis;
  21727. });
  21728. _registerModule(_modules, 'Core/Axis/LogarithmicAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21729. /* *
  21730. *
  21731. * (c) 2010-2021 Torstein Honsi
  21732. *
  21733. * License: www.highcharts.com/license
  21734. *
  21735. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21736. *
  21737. * */
  21738. const { addEvent, normalizeTickInterval, pick } = U;
  21739. /* *
  21740. *
  21741. * Class
  21742. *
  21743. * */
  21744. /**
  21745. * @private
  21746. */
  21747. var LogarithmicAxis;
  21748. (function (LogarithmicAxis) {
  21749. /* *
  21750. *
  21751. * Declarations
  21752. *
  21753. * */
  21754. /* *
  21755. *
  21756. * Constants
  21757. *
  21758. * */
  21759. const composedMembers = [];
  21760. /* *
  21761. *
  21762. * Functions
  21763. *
  21764. * */
  21765. /* eslint-disable valid-jsdoc */
  21766. /**
  21767. * Provides logarithmic support for axes.
  21768. * @private
  21769. */
  21770. function compose(AxisClass) {
  21771. if (U.pushUnique(composedMembers, AxisClass)) {
  21772. AxisClass.keepProps.push('logarithmic');
  21773. addEvent(AxisClass, 'init', onInit);
  21774. addEvent(AxisClass, 'afterInit', onAfterInit);
  21775. }
  21776. return AxisClass;
  21777. }
  21778. LogarithmicAxis.compose = compose;
  21779. /**
  21780. * @private
  21781. */
  21782. function onInit(e) {
  21783. const axis = this;
  21784. const options = e.userOptions;
  21785. let logarithmic = axis.logarithmic;
  21786. if (options.type !== 'logarithmic') {
  21787. axis.logarithmic = void 0;
  21788. }
  21789. else {
  21790. if (!logarithmic) {
  21791. logarithmic = axis.logarithmic = new Additions(axis);
  21792. }
  21793. }
  21794. }
  21795. /**
  21796. * @private
  21797. */
  21798. function onAfterInit() {
  21799. const axis = this;
  21800. const log = axis.logarithmic;
  21801. // extend logarithmic axis
  21802. if (log) {
  21803. axis.lin2val = function (num) {
  21804. return log.lin2log(num);
  21805. };
  21806. axis.val2lin = function (num) {
  21807. return log.log2lin(num);
  21808. };
  21809. }
  21810. }
  21811. /* *
  21812. *
  21813. * Class
  21814. *
  21815. * */
  21816. /**
  21817. * Provides logarithmic support for axes.
  21818. * @private
  21819. * @class
  21820. */
  21821. class Additions {
  21822. /* *
  21823. *
  21824. * Constructors
  21825. *
  21826. * */
  21827. constructor(axis) {
  21828. this.axis = axis;
  21829. }
  21830. /* *
  21831. *
  21832. * Functions
  21833. *
  21834. * */
  21835. /**
  21836. * Set the tick positions of a logarithmic axis.
  21837. */
  21838. getLogTickPositions(interval, min, max, minor) {
  21839. const log = this;
  21840. const axis = log.axis;
  21841. const axisLength = axis.len;
  21842. const options = axis.options;
  21843. // Since we use this method for both major and minor ticks,
  21844. // use a local variable and return the result
  21845. let positions = [];
  21846. // Reset
  21847. if (!minor) {
  21848. log.minorAutoInterval = void 0;
  21849. }
  21850. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  21851. if (interval >= 0.5) {
  21852. interval = Math.round(interval);
  21853. positions = axis.getLinearTickPositions(interval, min, max);
  21854. // Second case: We need intermediary ticks. For example
  21855. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  21856. }
  21857. else if (interval >= 0.08) {
  21858. const roundedMin = Math.floor(min);
  21859. let intermediate, i, j, len, pos, lastPos, break2;
  21860. if (interval > 0.3) {
  21861. intermediate = [1, 2, 4];
  21862. // 0.2 equals five minor ticks per 1, 10, 100 etc
  21863. }
  21864. else if (interval > 0.15) {
  21865. intermediate = [1, 2, 4, 6, 8];
  21866. }
  21867. else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  21868. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  21869. }
  21870. for (i = roundedMin; i < max + 1 && !break2; i++) {
  21871. len = intermediate.length;
  21872. for (j = 0; j < len && !break2; j++) {
  21873. pos = log.log2lin(log.lin2log(i) * intermediate[j]);
  21874. // #1670, lastPos is #3113
  21875. if (pos > min &&
  21876. (!minor || lastPos <= max) &&
  21877. typeof lastPos !== 'undefined') {
  21878. positions.push(lastPos);
  21879. }
  21880. if (lastPos > max) {
  21881. break2 = true;
  21882. }
  21883. lastPos = pos;
  21884. }
  21885. }
  21886. // Third case: We are so deep in between whole logarithmic values,
  21887. // that we might as well handle the tick positions like a linear
  21888. // axis. For example 1.01, 1.02, 1.03, 1.04.
  21889. }
  21890. else {
  21891. const realMin = log.lin2log(min), realMax = log.lin2log(max), tickIntervalOption = minor ?
  21892. axis.getMinorTickInterval() :
  21893. options.tickInterval, filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  21894. null :
  21895. tickIntervalOption, tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1), totalPixelLength = minor ?
  21896. axisLength / axis.tickPositions.length :
  21897. axisLength;
  21898. interval = pick(filteredTickIntervalOption, log.minorAutoInterval, (realMax - realMin) *
  21899. tickPixelIntervalOption / (totalPixelLength || 1));
  21900. interval = normalizeTickInterval(interval);
  21901. positions = axis.getLinearTickPositions(interval, realMin, realMax).map(log.log2lin);
  21902. if (!minor) {
  21903. log.minorAutoInterval = interval / 5;
  21904. }
  21905. }
  21906. // Set the axis-level tickInterval variable
  21907. if (!minor) {
  21908. axis.tickInterval = interval;
  21909. }
  21910. return positions;
  21911. }
  21912. lin2log(num) {
  21913. return Math.pow(10, num);
  21914. }
  21915. log2lin(num) {
  21916. return Math.log(num) / Math.LN10;
  21917. }
  21918. }
  21919. LogarithmicAxis.Additions = Additions;
  21920. })(LogarithmicAxis || (LogarithmicAxis = {}));
  21921. /* *
  21922. *
  21923. * Default Export
  21924. *
  21925. * */
  21926. return LogarithmicAxis;
  21927. });
  21928. _registerModule(_modules, 'Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21929. /* *
  21930. *
  21931. * (c) 2010-2021 Torstein Honsi
  21932. *
  21933. * License: www.highcharts.com/license
  21934. *
  21935. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21936. *
  21937. * */
  21938. const { erase, extend, isNumber } = U;
  21939. /* *
  21940. *
  21941. * Composition
  21942. *
  21943. * */
  21944. var PlotLineOrBandAxis;
  21945. (function (PlotLineOrBandAxis) {
  21946. /* *
  21947. *
  21948. * Declarations
  21949. *
  21950. * */
  21951. /* *
  21952. *
  21953. * Constants
  21954. *
  21955. * */
  21956. const composedMembers = [];
  21957. /* *
  21958. *
  21959. * Variables
  21960. *
  21961. * */
  21962. let PlotLineOrBandClass;
  21963. /* *
  21964. *
  21965. * Functions
  21966. *
  21967. * */
  21968. /**
  21969. * Add a plot band after render time.
  21970. *
  21971. * @sample highcharts/members/axis-addplotband/
  21972. * Toggle the plot band from a button
  21973. *
  21974. * @function Highcharts.Axis#addPlotBand
  21975. *
  21976. * @param {Highcharts.AxisPlotBandsOptions} options
  21977. * A configuration object for the plot band, as defined in
  21978. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  21979. *
  21980. * @return {Highcharts.PlotLineOrBand|undefined}
  21981. * The added plot band.
  21982. */
  21983. function addPlotBand(options) {
  21984. return this.addPlotBandOrLine(options, 'plotBands');
  21985. }
  21986. /**
  21987. * Add a plot band or plot line after render time. Called from
  21988. * addPlotBand and addPlotLine internally.
  21989. *
  21990. * @private
  21991. * @function Highcharts.Axis#addPlotBandOrLine
  21992. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  21993. * The plotBand or plotLine configuration object.
  21994. */
  21995. function addPlotBandOrLine(options, coll) {
  21996. const userOptions = this.userOptions;
  21997. let obj = new PlotLineOrBandClass(this, options);
  21998. if (this.visible) {
  21999. obj = obj.render();
  22000. }
  22001. if (obj) { // #2189
  22002. if (!this._addedPlotLB) {
  22003. this._addedPlotLB = true;
  22004. (userOptions.plotLines || [])
  22005. .concat(userOptions.plotBands || [])
  22006. .forEach((plotLineOptions) => {
  22007. this.addPlotBandOrLine(plotLineOptions);
  22008. });
  22009. }
  22010. // Add it to the user options for exporting and Axis.update
  22011. if (coll) {
  22012. // Workaround Microsoft/TypeScript issue #32693
  22013. const updatedOptions = (userOptions[coll] || []);
  22014. updatedOptions.push(options);
  22015. userOptions[coll] = updatedOptions;
  22016. }
  22017. this.plotLinesAndBands.push(obj);
  22018. }
  22019. return obj;
  22020. }
  22021. /**
  22022. * Add a plot line after render time.
  22023. *
  22024. * @sample highcharts/members/axis-addplotline/
  22025. * Toggle the plot line from a button
  22026. *
  22027. * @function Highcharts.Axis#addPlotLine
  22028. *
  22029. * @param {Highcharts.AxisPlotLinesOptions} options
  22030. * A configuration object for the plot line, as defined in
  22031. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  22032. *
  22033. * @return {Highcharts.PlotLineOrBand|undefined}
  22034. * The added plot line.
  22035. */
  22036. function addPlotLine(options) {
  22037. return this.addPlotBandOrLine(options, 'plotLines');
  22038. }
  22039. /**
  22040. * @private
  22041. */
  22042. function compose(PlotLineOrBandType, AxisClass) {
  22043. if (!PlotLineOrBandClass) {
  22044. PlotLineOrBandClass = PlotLineOrBandType;
  22045. }
  22046. if (U.pushUnique(composedMembers, AxisClass)) {
  22047. extend(AxisClass.prototype, {
  22048. addPlotBand,
  22049. addPlotLine,
  22050. addPlotBandOrLine,
  22051. getPlotBandPath,
  22052. removePlotBand,
  22053. removePlotLine,
  22054. removePlotBandOrLine
  22055. });
  22056. }
  22057. return AxisClass;
  22058. }
  22059. PlotLineOrBandAxis.compose = compose;
  22060. /**
  22061. * Internal function to create the SVG path definition for a plot band.
  22062. *
  22063. * @function Highcharts.Axis#getPlotBandPath
  22064. *
  22065. * @param {number} from
  22066. * The axis value to start from.
  22067. *
  22068. * @param {number} to
  22069. * The axis value to end on.
  22070. *
  22071. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  22072. * The plotBand or plotLine configuration object.
  22073. *
  22074. * @return {Highcharts.SVGPathArray}
  22075. * The SVG path definition in array form.
  22076. */
  22077. function getPlotBandPath(from, to, options = this.options) {
  22078. const toPath = this.getPlotLinePath({
  22079. value: to,
  22080. force: true,
  22081. acrossPanes: options.acrossPanes
  22082. }), result = [], horiz = this.horiz, outside = !isNumber(this.min) ||
  22083. !isNumber(this.max) ||
  22084. (from < this.min && to < this.min) ||
  22085. (from > this.max && to > this.max);
  22086. let path = this.getPlotLinePath({
  22087. value: from,
  22088. force: true,
  22089. acrossPanes: options.acrossPanes
  22090. }), i,
  22091. // #4964 check if chart is inverted or plotband is on yAxis
  22092. plus = 1, isFlat;
  22093. if (path && toPath) {
  22094. // Flat paths don't need labels (#3836)
  22095. if (outside) {
  22096. isFlat = path.toString() === toPath.toString();
  22097. plus = 0;
  22098. }
  22099. // Go over each subpath - for panes in Highcharts Stock
  22100. for (i = 0; i < path.length; i += 2) {
  22101. const pathStart = path[i], pathEnd = path[i + 1], toPathStart = toPath[i], toPathEnd = toPath[i + 1];
  22102. // Type checking all affected path segments. Consider
  22103. // something smarter.
  22104. if ((pathStart[0] === 'M' || pathStart[0] === 'L') &&
  22105. (pathEnd[0] === 'M' || pathEnd[0] === 'L') &&
  22106. (toPathStart[0] === 'M' || toPathStart[0] === 'L') &&
  22107. (toPathEnd[0] === 'M' || toPathEnd[0] === 'L')) {
  22108. // Add 1 pixel when coordinates are the same
  22109. if (horiz && toPathStart[1] === pathStart[1]) {
  22110. toPathStart[1] += plus;
  22111. toPathEnd[1] += plus;
  22112. }
  22113. else if (!horiz && toPathStart[2] === pathStart[2]) {
  22114. toPathStart[2] += plus;
  22115. toPathEnd[2] += plus;
  22116. }
  22117. result.push(['M', pathStart[1], pathStart[2]], ['L', pathEnd[1], pathEnd[2]], ['L', toPathEnd[1], toPathEnd[2]], ['L', toPathStart[1], toPathStart[2]], ['Z']);
  22118. }
  22119. result.isFlat = isFlat;
  22120. }
  22121. }
  22122. else { // outside the axis area
  22123. path = null;
  22124. }
  22125. return result;
  22126. }
  22127. /**
  22128. * Remove a plot band by its id.
  22129. *
  22130. * @sample highcharts/members/axis-removeplotband/
  22131. * Remove plot band by id
  22132. * @sample highcharts/members/axis-addplotband/
  22133. * Toggle the plot band from a button
  22134. *
  22135. * @function Highcharts.Axis#removePlotBand
  22136. *
  22137. * @param {string} id
  22138. * The plot band's `id` as given in the original configuration
  22139. * object or in the `addPlotBand` option.
  22140. */
  22141. function removePlotBand(id) {
  22142. this.removePlotBandOrLine(id);
  22143. }
  22144. /**
  22145. * Remove a plot band or plot line from the chart by id. Called
  22146. * internally from `removePlotBand` and `removePlotLine`.
  22147. * @private
  22148. * @function Highcharts.Axis#removePlotBandOrLine
  22149. */
  22150. function removePlotBandOrLine(id) {
  22151. const plotLinesAndBands = this.plotLinesAndBands, options = this.options, userOptions = this.userOptions;
  22152. if (plotLinesAndBands) { // #15639
  22153. let i = plotLinesAndBands.length;
  22154. while (i--) {
  22155. if (plotLinesAndBands[i].id === id) {
  22156. plotLinesAndBands[i].destroy();
  22157. }
  22158. }
  22159. ([
  22160. options.plotLines || [],
  22161. userOptions.plotLines || [],
  22162. options.plotBands || [],
  22163. userOptions.plotBands || []
  22164. ]).forEach(function (arr) {
  22165. i = arr.length;
  22166. while (i--) {
  22167. if ((arr[i] || {}).id === id) {
  22168. erase(arr, arr[i]);
  22169. }
  22170. }
  22171. });
  22172. }
  22173. }
  22174. /**
  22175. * Remove a plot line by its id.
  22176. *
  22177. * @sample highcharts/xaxis/plotlines-id/
  22178. * Remove plot line by id
  22179. * @sample highcharts/members/axis-addplotline/
  22180. * Toggle the plot line from a button
  22181. *
  22182. * @function Highcharts.Axis#removePlotLine
  22183. *
  22184. * @param {string} id
  22185. * The plot line's `id` as given in the original configuration
  22186. * object or in the `addPlotLine` option.
  22187. */
  22188. function removePlotLine(id) {
  22189. this.removePlotBandOrLine(id);
  22190. }
  22191. })(PlotLineOrBandAxis || (PlotLineOrBandAxis = {}));
  22192. /* *
  22193. *
  22194. * Default Export
  22195. *
  22196. * */
  22197. return PlotLineOrBandAxis;
  22198. });
  22199. _registerModule(_modules, 'Core/Axis/PlotLineOrBand/PlotLineOrBand.js', [_modules['Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js'], _modules['Core/Utilities.js']], function (PlotLineOrBandAxis, U) {
  22200. /* *
  22201. *
  22202. * (c) 2010-2021 Torstein Honsi
  22203. *
  22204. * License: www.highcharts.com/license
  22205. *
  22206. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22207. *
  22208. * */
  22209. const { arrayMax, arrayMin, defined, destroyObjectProperties, erase, fireEvent, merge, objectEach, pick } = U;
  22210. /* *
  22211. *
  22212. * Class
  22213. *
  22214. * */
  22215. /**
  22216. * The object wrapper for plot lines and plot bands
  22217. *
  22218. * @class
  22219. * @name Highcharts.PlotLineOrBand
  22220. *
  22221. * @param {Highcharts.Axis} axis
  22222. * Related axis.
  22223. *
  22224. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
  22225. * Options to use.
  22226. */
  22227. class PlotLineOrBand {
  22228. /* *
  22229. *
  22230. * Static Functions
  22231. *
  22232. * */
  22233. static compose(AxisClass) {
  22234. return PlotLineOrBandAxis.compose(PlotLineOrBand, AxisClass);
  22235. }
  22236. /* *
  22237. *
  22238. * Constructor
  22239. *
  22240. * */
  22241. constructor(axis, options) {
  22242. this.axis = axis;
  22243. if (options) {
  22244. this.options = options;
  22245. this.id = options.id;
  22246. }
  22247. }
  22248. /* *
  22249. *
  22250. * Functions
  22251. *
  22252. * */
  22253. /* eslint-disable no-invalid-this, valid-jsdoc */
  22254. /**
  22255. * Render the plot line or plot band. If it is already existing,
  22256. * move it.
  22257. * @private
  22258. * @function Highcharts.PlotLineOrBand#render
  22259. */
  22260. render() {
  22261. fireEvent(this, 'render');
  22262. const plotLine = this, axis = plotLine.axis, horiz = axis.horiz, log = axis.logarithmic, options = plotLine.options, color = options.color, zIndex = pick(options.zIndex, 0), events = options.events, groupAttribs = {}, renderer = axis.chart.renderer;
  22263. let optionsLabel = options.label, label = plotLine.label, to = options.to, from = options.from, value = options.value, svgElem = plotLine.svgElem, path = [], group;
  22264. const isBand = defined(from) && defined(to), isLine = defined(value), isNew = !svgElem, attribs = {
  22265. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  22266. (options.className || '')
  22267. };
  22268. let groupName = isBand ? 'bands' : 'lines';
  22269. // logarithmic conversion
  22270. if (log) {
  22271. from = log.log2lin(from);
  22272. to = log.log2lin(to);
  22273. value = log.log2lin(value);
  22274. }
  22275. // Set the presentational attributes
  22276. if (!axis.chart.styledMode) {
  22277. if (isLine) {
  22278. attribs.stroke = color || "#999999" /* Palette.neutralColor40 */;
  22279. attribs['stroke-width'] = pick(options.width, 1);
  22280. if (options.dashStyle) {
  22281. attribs.dashstyle =
  22282. options.dashStyle;
  22283. }
  22284. }
  22285. else if (isBand) { // plot band
  22286. attribs.fill = color || "#e6e9ff" /* Palette.highlightColor10 */;
  22287. if (options.borderWidth) {
  22288. attribs.stroke = options.borderColor;
  22289. attribs['stroke-width'] = options.borderWidth;
  22290. }
  22291. }
  22292. }
  22293. // Grouping and zIndex
  22294. groupAttribs.zIndex = zIndex;
  22295. groupName += '-' + zIndex;
  22296. group = axis.plotLinesAndBandsGroups[groupName];
  22297. if (!group) {
  22298. axis.plotLinesAndBandsGroups[groupName] = group =
  22299. renderer.g('plot-' + groupName)
  22300. .attr(groupAttribs).add();
  22301. }
  22302. // Create the path
  22303. if (isNew) {
  22304. /**
  22305. * SVG element of the plot line or band.
  22306. *
  22307. * @name Highcharts.PlotLineOrBand#svgElem
  22308. * @type {Highcharts.SVGElement}
  22309. */
  22310. plotLine.svgElem = svgElem = renderer
  22311. .path()
  22312. .attr(attribs)
  22313. .add(group);
  22314. }
  22315. // Set the path or return
  22316. if (isLine) {
  22317. path = axis.getPlotLinePath({
  22318. value: value,
  22319. lineWidth: svgElem.strokeWidth(),
  22320. acrossPanes: options.acrossPanes
  22321. });
  22322. }
  22323. else if (isBand) { // plot band
  22324. path = axis.getPlotBandPath(from, to, options);
  22325. }
  22326. else {
  22327. return;
  22328. }
  22329. // common for lines and bands
  22330. // Add events only if they were not added before.
  22331. if (!plotLine.eventsAdded && events) {
  22332. objectEach(events, function (event, eventType) {
  22333. svgElem.on(eventType, function (e) {
  22334. events[eventType].apply(plotLine, [e]);
  22335. });
  22336. });
  22337. plotLine.eventsAdded = true;
  22338. }
  22339. if ((isNew || !svgElem.d) && path && path.length) {
  22340. svgElem.attr({ d: path });
  22341. }
  22342. else if (svgElem) {
  22343. if (path) {
  22344. svgElem.show();
  22345. svgElem.animate({ d: path });
  22346. }
  22347. else if (svgElem.d) {
  22348. svgElem.hide();
  22349. if (label) {
  22350. plotLine.label = label = label.destroy();
  22351. }
  22352. }
  22353. }
  22354. // the plot band/line label
  22355. if (optionsLabel &&
  22356. (defined(optionsLabel.text) || defined(optionsLabel.formatter)) &&
  22357. path &&
  22358. path.length &&
  22359. axis.width > 0 &&
  22360. axis.height > 0 &&
  22361. !path.isFlat) {
  22362. // apply defaults
  22363. optionsLabel = merge({
  22364. align: horiz && isBand && 'center',
  22365. x: horiz ? !isBand && 4 : 10,
  22366. verticalAlign: !horiz && isBand && 'middle',
  22367. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  22368. rotation: horiz && !isBand && 90
  22369. }, optionsLabel);
  22370. this.renderLabel(optionsLabel, path, isBand, zIndex);
  22371. }
  22372. else if (label) { // move out of sight
  22373. label.hide();
  22374. }
  22375. // chainable
  22376. return plotLine;
  22377. }
  22378. /**
  22379. * Render and align label for plot line or band.
  22380. * @private
  22381. * @function Highcharts.PlotLineOrBand#renderLabel
  22382. */
  22383. renderLabel(optionsLabel, path, isBand, zIndex) {
  22384. const plotLine = this, axis = plotLine.axis, renderer = axis.chart.renderer;
  22385. let label = plotLine.label;
  22386. // add the SVG element
  22387. if (!label) {
  22388. /**
  22389. * SVG element of the label.
  22390. *
  22391. * @name Highcharts.PlotLineOrBand#label
  22392. * @type {Highcharts.SVGElement}
  22393. */
  22394. plotLine.label = label = renderer
  22395. .text(this.getLabelText(optionsLabel), 0, 0, optionsLabel.useHTML)
  22396. .attr({
  22397. align: optionsLabel.textAlign || optionsLabel.align,
  22398. rotation: optionsLabel.rotation,
  22399. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  22400. '-label ' + (optionsLabel.className || ''),
  22401. zIndex
  22402. })
  22403. .add();
  22404. if (!axis.chart.styledMode) {
  22405. label.css(merge({
  22406. fontSize: '0.8em',
  22407. textOverflow: 'ellipsis'
  22408. }, optionsLabel.style));
  22409. }
  22410. }
  22411. // get the bounding box and align the label
  22412. // #3000 changed to better handle choice between plotband or plotline
  22413. const xBounds = path.xBounds ||
  22414. [path[0][1], path[1][1], (isBand ? path[2][1] : path[0][1])];
  22415. const yBounds = path.yBounds ||
  22416. [path[0][2], path[1][2], (isBand ? path[2][2] : path[0][2])];
  22417. const x = arrayMin(xBounds);
  22418. const y = arrayMin(yBounds);
  22419. label.align(optionsLabel, false, {
  22420. x,
  22421. y,
  22422. width: arrayMax(xBounds) - x,
  22423. height: arrayMax(yBounds) - y
  22424. });
  22425. if (!label.alignValue || label.alignValue === 'left') {
  22426. const width = optionsLabel.clip ?
  22427. axis.width : axis.chart.chartWidth;
  22428. label.css({
  22429. width: (label.rotation === 90 ?
  22430. axis.height - (label.alignAttr.y - axis.top) :
  22431. width - (label.alignAttr.x - axis.left)) + 'px'
  22432. });
  22433. }
  22434. label.show(true);
  22435. }
  22436. /**
  22437. * Get label's text content.
  22438. * @private
  22439. * @function Highcharts.PlotLineOrBand#getLabelText
  22440. */
  22441. getLabelText(optionsLabel) {
  22442. return defined(optionsLabel.formatter) ?
  22443. optionsLabel.formatter
  22444. .call(this) :
  22445. optionsLabel.text;
  22446. }
  22447. /**
  22448. * Remove the plot line or band.
  22449. *
  22450. * @function Highcharts.PlotLineOrBand#destroy
  22451. */
  22452. destroy() {
  22453. // remove it from the lookup
  22454. erase(this.axis.plotLinesAndBands, this);
  22455. delete this.axis;
  22456. destroyObjectProperties(this);
  22457. }
  22458. }
  22459. /* *
  22460. *
  22461. * Default Export
  22462. *
  22463. * */
  22464. /* *
  22465. *
  22466. * API Options
  22467. *
  22468. * */
  22469. /**
  22470. * Options for plot bands on axes.
  22471. *
  22472. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  22473. */
  22474. /**
  22475. * Options for plot band labels on axes.
  22476. *
  22477. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  22478. */
  22479. /**
  22480. * Options for plot lines on axes.
  22481. *
  22482. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  22483. */
  22484. /**
  22485. * Options for plot line labels on axes.
  22486. *
  22487. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  22488. */
  22489. ('');
  22490. /* *
  22491. *
  22492. * API Options
  22493. *
  22494. * */
  22495. /**
  22496. * An array of colored bands stretching across the plot area marking an
  22497. * interval on the axis.
  22498. *
  22499. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  22500. * class in addition to the `className` option.
  22501. *
  22502. * @productdesc {highcharts}
  22503. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  22504. * perimeter of the gauge.
  22505. *
  22506. * @type {Array<*>}
  22507. * @product highcharts highstock gantt
  22508. * @apioption xAxis.plotBands
  22509. */
  22510. /**
  22511. * Flag to decide if plotBand should be rendered across all panes.
  22512. *
  22513. * @since 7.1.2
  22514. * @product highstock
  22515. * @type {boolean}
  22516. * @default true
  22517. * @apioption xAxis.plotBands.acrossPanes
  22518. */
  22519. /**
  22520. * Border color for the plot band. Also requires `borderWidth` to be set.
  22521. *
  22522. * @type {Highcharts.ColorString}
  22523. * @apioption xAxis.plotBands.borderColor
  22524. */
  22525. /**
  22526. * Border width for the plot band. Also requires `borderColor` to be set.
  22527. *
  22528. * @type {number}
  22529. * @default 0
  22530. * @apioption xAxis.plotBands.borderWidth
  22531. */
  22532. /**
  22533. * A custom class name, in addition to the default `highcharts-plot-band`,
  22534. * to apply to each individual band.
  22535. *
  22536. * @type {string}
  22537. * @since 5.0.0
  22538. * @apioption xAxis.plotBands.className
  22539. */
  22540. /**
  22541. * The color of the plot band.
  22542. *
  22543. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22544. * Color band
  22545. * @sample {highstock} stock/xaxis/plotbands/
  22546. * Plot band on Y axis
  22547. *
  22548. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  22549. * @default #e6e9ff
  22550. * @apioption xAxis.plotBands.color
  22551. */
  22552. /**
  22553. * An object defining mouse events for the plot band. Supported properties
  22554. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  22555. *
  22556. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  22557. * Mouse events demonstrated
  22558. *
  22559. * @since 1.2
  22560. * @apioption xAxis.plotBands.events
  22561. */
  22562. /**
  22563. * Click event on a plot band.
  22564. *
  22565. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22566. * @apioption xAxis.plotBands.events.click
  22567. */
  22568. /**
  22569. * Mouse move event on a plot band.
  22570. *
  22571. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22572. * @apioption xAxis.plotBands.events.mousemove
  22573. */
  22574. /**
  22575. * Mouse out event on the corner of a plot band.
  22576. *
  22577. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22578. * @apioption xAxis.plotBands.events.mouseout
  22579. */
  22580. /**
  22581. * Mouse over event on a plot band.
  22582. *
  22583. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22584. * @apioption xAxis.plotBands.events.mouseover
  22585. */
  22586. /**
  22587. * The start position of the plot band in axis units.
  22588. *
  22589. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22590. * Datetime axis
  22591. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  22592. * Categorized axis
  22593. * @sample {highstock} stock/xaxis/plotbands/
  22594. * Plot band on Y axis
  22595. *
  22596. * @type {number}
  22597. * @apioption xAxis.plotBands.from
  22598. */
  22599. /**
  22600. * An id used for identifying the plot band in Axis.removePlotBand.
  22601. *
  22602. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  22603. * Remove plot band by id
  22604. * @sample {highstock} highcharts/xaxis/plotbands-id/
  22605. * Remove plot band by id
  22606. *
  22607. * @type {string}
  22608. * @apioption xAxis.plotBands.id
  22609. */
  22610. /**
  22611. * The end position of the plot band in axis units.
  22612. *
  22613. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22614. * Datetime axis
  22615. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  22616. * Categorized axis
  22617. * @sample {highstock} stock/xaxis/plotbands/
  22618. * Plot band on Y axis
  22619. *
  22620. * @type {number}
  22621. * @apioption xAxis.plotBands.to
  22622. */
  22623. /**
  22624. * The z index of the plot band within the chart, relative to other
  22625. * elements. Using the same z index as another element may give
  22626. * unpredictable results, as the last rendered element will be on top.
  22627. * Values from 0 to 20 make sense.
  22628. *
  22629. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22630. * Behind plot lines by default
  22631. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  22632. * Above plot lines
  22633. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  22634. * Above plot lines and series
  22635. *
  22636. * @type {number}
  22637. * @since 1.2
  22638. * @apioption xAxis.plotBands.zIndex
  22639. */
  22640. /**
  22641. * Text labels for the plot bands
  22642. *
  22643. * @product highcharts highstock gantt
  22644. * @apioption xAxis.plotBands.label
  22645. */
  22646. /**
  22647. * Horizontal alignment of the label. Can be one of "left", "center" or
  22648. * "right".
  22649. *
  22650. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  22651. * Aligned to the right
  22652. * @sample {highstock} stock/xaxis/plotbands-label/
  22653. * Plot band with labels
  22654. *
  22655. * @type {Highcharts.AlignValue}
  22656. * @default center
  22657. * @since 2.1
  22658. * @apioption xAxis.plotBands.label.align
  22659. */
  22660. /**
  22661. * Rotation of the text label in degrees .
  22662. *
  22663. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  22664. * Vertical text
  22665. *
  22666. * @type {number}
  22667. * @default 0
  22668. * @since 2.1
  22669. * @apioption xAxis.plotBands.label.rotation
  22670. */
  22671. /**
  22672. * CSS styles for the text label.
  22673. *
  22674. * In styled mode, the labels are styled by the
  22675. * `.highcharts-plot-band-label` class.
  22676. *
  22677. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  22678. * Blue and bold label
  22679. *
  22680. * @type {Highcharts.CSSObject}
  22681. * @since 2.1
  22682. * @apioption xAxis.plotBands.label.style
  22683. */
  22684. /**
  22685. * The string text itself. A subset of HTML is supported.
  22686. *
  22687. * @type {string}
  22688. * @since 2.1
  22689. * @apioption xAxis.plotBands.label.text
  22690. */
  22691. /**
  22692. * The text alignment for the label. While `align` determines where the
  22693. * texts anchor point is placed within the plot band, `textAlign` determines
  22694. * how the text is aligned against its anchor point. Possible values are
  22695. * "left", "center" and "right". Defaults to the same as the `align` option.
  22696. *
  22697. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  22698. * Vertical text in center position but text-aligned left
  22699. *
  22700. * @type {Highcharts.AlignValue}
  22701. * @since 2.1
  22702. * @apioption xAxis.plotBands.label.textAlign
  22703. */
  22704. /**
  22705. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  22706. * to render the labels.
  22707. *
  22708. * @type {boolean}
  22709. * @default false
  22710. * @since 3.0.3
  22711. * @apioption xAxis.plotBands.label.useHTML
  22712. */
  22713. /**
  22714. * Vertical alignment of the label relative to the plot band. Can be one of
  22715. * "top", "middle" or "bottom".
  22716. *
  22717. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  22718. * Vertically centered label
  22719. * @sample {highstock} stock/xaxis/plotbands-label/
  22720. * Plot band with labels
  22721. *
  22722. * @type {Highcharts.VerticalAlignValue}
  22723. * @default top
  22724. * @since 2.1
  22725. * @apioption xAxis.plotBands.label.verticalAlign
  22726. */
  22727. /**
  22728. * Horizontal position relative the alignment. Default varies by
  22729. * orientation.
  22730. *
  22731. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  22732. * Aligned 10px from the right edge
  22733. * @sample {highstock} stock/xaxis/plotbands-label/
  22734. * Plot band with labels
  22735. *
  22736. * @type {number}
  22737. * @since 2.1
  22738. * @apioption xAxis.plotBands.label.x
  22739. */
  22740. /**
  22741. * Vertical position of the text baseline relative to the alignment. Default
  22742. * varies by orientation.
  22743. *
  22744. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  22745. * Label on x axis
  22746. * @sample {highstock} stock/xaxis/plotbands-label/
  22747. * Plot band with labels
  22748. *
  22749. * @type {number}
  22750. * @since 2.1
  22751. * @apioption xAxis.plotBands.label.y
  22752. */
  22753. /**
  22754. * An array of lines stretching across the plot area, marking a specific
  22755. * value on one of the axes.
  22756. *
  22757. * In styled mode, the plot lines are styled by the
  22758. * `.highcharts-plot-line` class in addition to the `className` option.
  22759. *
  22760. * @type {Array<*>}
  22761. * @product highcharts highstock gantt
  22762. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22763. * Basic plot line
  22764. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  22765. * Solid gauge plot line
  22766. * @apioption xAxis.plotLines
  22767. */
  22768. /**
  22769. * Flag to decide if plotLine should be rendered across all panes.
  22770. *
  22771. * @sample {highstock} stock/xaxis/plotlines-acrosspanes/
  22772. * Plot lines on different panes
  22773. *
  22774. * @since 7.1.2
  22775. * @product highstock
  22776. * @type {boolean}
  22777. * @default true
  22778. * @apioption xAxis.plotLines.acrossPanes
  22779. */
  22780. /**
  22781. * A custom class name, in addition to the default `highcharts-plot-line`,
  22782. * to apply to each individual line.
  22783. *
  22784. * @type {string}
  22785. * @since 5.0.0
  22786. * @apioption xAxis.plotLines.className
  22787. */
  22788. /**
  22789. * The color of the line.
  22790. *
  22791. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22792. * A red line from X axis
  22793. * @sample {highstock} stock/xaxis/plotlines/
  22794. * Plot line on Y axis
  22795. *
  22796. * @type {Highcharts.ColorString}
  22797. * @default #999999
  22798. * @apioption xAxis.plotLines.color
  22799. */
  22800. /**
  22801. * The dashing or dot style for the plot line. For possible values see
  22802. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  22803. *
  22804. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  22805. * Dash and dot pattern
  22806. * @sample {highstock} stock/xaxis/plotlines/
  22807. * Plot line on Y axis
  22808. *
  22809. * @type {Highcharts.DashStyleValue}
  22810. * @default Solid
  22811. * @since 1.2
  22812. * @apioption xAxis.plotLines.dashStyle
  22813. */
  22814. /**
  22815. * An object defining mouse events for the plot line. Supported
  22816. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  22817. *
  22818. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  22819. * Mouse events demonstrated
  22820. *
  22821. * @since 1.2
  22822. * @apioption xAxis.plotLines.events
  22823. */
  22824. /**
  22825. * Click event on a plot band.
  22826. *
  22827. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22828. * @apioption xAxis.plotLines.events.click
  22829. */
  22830. /**
  22831. * Mouse move event on a plot band.
  22832. *
  22833. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22834. * @apioption xAxis.plotLines.events.mousemove
  22835. */
  22836. /**
  22837. * Mouse out event on the corner of a plot band.
  22838. *
  22839. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22840. * @apioption xAxis.plotLines.events.mouseout
  22841. */
  22842. /**
  22843. * Mouse over event on a plot band.
  22844. *
  22845. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22846. * @apioption xAxis.plotLines.events.mouseover
  22847. */
  22848. /**
  22849. * An id used for identifying the plot line in Axis.removePlotLine.
  22850. *
  22851. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  22852. * Remove plot line by id
  22853. *
  22854. * @type {string}
  22855. * @apioption xAxis.plotLines.id
  22856. */
  22857. /**
  22858. * The position of the line in axis units.
  22859. *
  22860. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22861. * Between two categories on X axis
  22862. * @sample {highstock} stock/xaxis/plotlines/
  22863. * Plot line on Y axis
  22864. *
  22865. * @type {number}
  22866. * @apioption xAxis.plotLines.value
  22867. */
  22868. /**
  22869. * The width or thickness of the plot line.
  22870. *
  22871. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22872. * 2px wide line from X axis
  22873. * @sample {highstock} stock/xaxis/plotlines/
  22874. * Plot line on Y axis
  22875. *
  22876. * @type {number}
  22877. * @default 2
  22878. * @apioption xAxis.plotLines.width
  22879. */
  22880. /**
  22881. * The z index of the plot line within the chart.
  22882. *
  22883. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  22884. * Behind plot lines by default
  22885. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  22886. * Above plot lines
  22887. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  22888. * Above plot lines and series
  22889. *
  22890. * @type {number}
  22891. * @since 1.2
  22892. * @apioption xAxis.plotLines.zIndex
  22893. */
  22894. /**
  22895. * Text labels for the plot bands
  22896. *
  22897. * @apioption xAxis.plotLines.label
  22898. */
  22899. /**
  22900. * Horizontal alignment of the label. Can be one of "left", "center" or
  22901. * "right".
  22902. *
  22903. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  22904. * Aligned to the right
  22905. * @sample {highstock} stock/xaxis/plotlines/
  22906. * Plot line on Y axis
  22907. *
  22908. * @type {Highcharts.AlignValue}
  22909. * @default left
  22910. * @since 2.1
  22911. * @apioption xAxis.plotLines.label.align
  22912. */
  22913. /**
  22914. * Whether to hide labels that are outside the plot area.
  22915. *
  22916. * @type {boolean}
  22917. * @default false
  22918. * @since 10.3.3
  22919. * @apioption xAxis.plotLines.labels.clip
  22920. */
  22921. /**
  22922. * Callback JavaScript function to format the label. Useful properties like
  22923. * the value of plot line or the range of plot band (`from` & `to`
  22924. * properties) can be found in `this.options` object.
  22925. *
  22926. * @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
  22927. * Label formatters for plot line and plot band.
  22928. * @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
  22929. * @apioption xAxis.plotLines.label.formatter
  22930. */
  22931. /**
  22932. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  22933. * lines and 90 for vertical lines.
  22934. *
  22935. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  22936. * Slanted text
  22937. *
  22938. * @type {number}
  22939. * @since 2.1
  22940. * @apioption xAxis.plotLines.label.rotation
  22941. */
  22942. /**
  22943. * CSS styles for the text label.
  22944. *
  22945. * In styled mode, the labels are styled by the
  22946. * `.highcharts-plot-line-label` class.
  22947. *
  22948. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  22949. * Blue and bold label
  22950. *
  22951. * @type {Highcharts.CSSObject}
  22952. * @since 2.1
  22953. * @apioption xAxis.plotLines.label.style
  22954. */
  22955. /**
  22956. * The text itself. A subset of HTML is supported.
  22957. *
  22958. * @type {string}
  22959. * @since 2.1
  22960. * @apioption xAxis.plotLines.label.text
  22961. */
  22962. /**
  22963. * The text alignment for the label. While `align` determines where the
  22964. * texts anchor point is placed within the plot band, `textAlign` determines
  22965. * how the text is aligned against its anchor point. Possible values are
  22966. * "left", "center" and "right". Defaults to the same as the `align` option.
  22967. *
  22968. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  22969. * Text label in bottom position
  22970. *
  22971. * @type {Highcharts.AlignValue}
  22972. * @since 2.1
  22973. * @apioption xAxis.plotLines.label.textAlign
  22974. */
  22975. /**
  22976. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  22977. * to render the labels.
  22978. *
  22979. * @type {boolean}
  22980. * @default false
  22981. * @since 3.0.3
  22982. * @apioption xAxis.plotLines.label.useHTML
  22983. */
  22984. /**
  22985. * Vertical alignment of the label relative to the plot line. Can be
  22986. * one of "top", "middle" or "bottom".
  22987. *
  22988. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  22989. * Vertically centered label
  22990. *
  22991. * @type {Highcharts.VerticalAlignValue}
  22992. * @default {highcharts} top
  22993. * @default {highstock} top
  22994. * @since 2.1
  22995. * @apioption xAxis.plotLines.label.verticalAlign
  22996. */
  22997. /**
  22998. * Horizontal position relative the alignment. Default varies by
  22999. * orientation.
  23000. *
  23001. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  23002. * Aligned 10px from the right edge
  23003. * @sample {highstock} stock/xaxis/plotlines/
  23004. * Plot line on Y axis
  23005. *
  23006. * @type {number}
  23007. * @since 2.1
  23008. * @apioption xAxis.plotLines.label.x
  23009. */
  23010. /**
  23011. * Vertical position of the text baseline relative to the alignment. Default
  23012. * varies by orientation.
  23013. *
  23014. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  23015. * Label below the plot line
  23016. * @sample {highstock} stock/xaxis/plotlines/
  23017. * Plot line on Y axis
  23018. *
  23019. * @type {number}
  23020. * @since 2.1
  23021. * @apioption xAxis.plotLines.label.y
  23022. */
  23023. /**
  23024. * @type {Array<*>}
  23025. * @extends xAxis.plotBands
  23026. * @apioption yAxis.plotBands
  23027. */
  23028. /**
  23029. * In a gauge chart, this option determines the inner radius of the
  23030. * plot band that stretches along the perimeter. It can be given as
  23031. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  23032. * By default, the inner radius is controlled by the [thickness](
  23033. * #yAxis.plotBands.thickness) option.
  23034. *
  23035. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23036. * Gauge plot band
  23037. *
  23038. * @type {number|string}
  23039. * @since 2.3
  23040. * @product highcharts
  23041. * @apioption yAxis.plotBands.innerRadius
  23042. */
  23043. /**
  23044. * In a gauge chart, this option determines the outer radius of the
  23045. * plot band that stretches along the perimeter. It can be given as
  23046. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  23047. *
  23048. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23049. * Gauge plot band
  23050. *
  23051. * @type {number|string}
  23052. * @default 100%
  23053. * @since 2.3
  23054. * @product highcharts
  23055. * @apioption yAxis.plotBands.outerRadius
  23056. */
  23057. /**
  23058. * In a gauge chart, this option sets the width of the plot band
  23059. * stretching along the perimeter. It can be given as a percentage
  23060. * string, like `"10%"`, or as a pixel number, like `10`. The default
  23061. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  23062. * thus making the plot band act as a background for the tick markers.
  23063. *
  23064. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23065. * Gauge plot band
  23066. *
  23067. * @type {number|string}
  23068. * @default 10
  23069. * @since 2.3
  23070. * @product highcharts
  23071. * @apioption yAxis.plotBands.thickness
  23072. */
  23073. /**
  23074. * @type {Array<*>}
  23075. * @extends xAxis.plotLines
  23076. * @apioption yAxis.plotLines
  23077. */
  23078. (''); // keeps doclets above in JS file
  23079. return PlotLineOrBand;
  23080. });
  23081. _registerModule(_modules, 'Core/Tooltip.js', [_modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (F, H, R, RendererRegistry, U) {
  23082. /* *
  23083. *
  23084. * (c) 2010-2021 Torstein Honsi
  23085. *
  23086. * License: www.highcharts.com/license
  23087. *
  23088. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23089. *
  23090. * */
  23091. const { format } = F;
  23092. const { doc, isSafari } = H;
  23093. const { distribute } = R;
  23094. const { addEvent, clamp, css, discardElement, extend, fireEvent, isArray, isNumber, isString, merge, pick, splat, syncTimeout } = U;
  23095. /* *
  23096. *
  23097. * Class
  23098. *
  23099. * */
  23100. /* eslint-disable no-invalid-this, valid-jsdoc */
  23101. /**
  23102. * Tooltip of a chart.
  23103. *
  23104. * @class
  23105. * @name Highcharts.Tooltip
  23106. *
  23107. * @param {Highcharts.Chart} chart
  23108. * The chart instance.
  23109. *
  23110. * @param {Highcharts.TooltipOptions} options
  23111. * Tooltip options.
  23112. */
  23113. class Tooltip {
  23114. /* *
  23115. *
  23116. * Constructors
  23117. *
  23118. * */
  23119. constructor(chart, options) {
  23120. /* *
  23121. *
  23122. * Properties
  23123. *
  23124. * */
  23125. this.allowShared = true;
  23126. this.container = void 0;
  23127. this.crosshairs = [];
  23128. this.distance = 0;
  23129. this.isHidden = true;
  23130. this.isSticky = false;
  23131. this.now = {};
  23132. this.options = {};
  23133. this.outside = false;
  23134. this.chart = chart;
  23135. this.init(chart, options);
  23136. }
  23137. /* *
  23138. *
  23139. * Functions
  23140. *
  23141. * */
  23142. /**
  23143. * Build the body (lines) of the tooltip by iterating over the items and
  23144. * returning one entry for each item, abstracting this functionality allows
  23145. * to easily overwrite and extend it.
  23146. *
  23147. * @private
  23148. * @function Highcharts.Tooltip#bodyFormatter
  23149. */
  23150. bodyFormatter(items) {
  23151. return items.map(function (item) {
  23152. const tooltipOptions = item.series.tooltipOptions;
  23153. return (tooltipOptions[(item.point.formatPrefix || 'point') + 'Formatter'] ||
  23154. item.point.tooltipFormatter).call(item.point, tooltipOptions[(item.point.formatPrefix || 'point') + 'Format'] || '');
  23155. });
  23156. }
  23157. /**
  23158. * Destroy the single tooltips in a split tooltip.
  23159. * If the tooltip is active then it is not destroyed, unless forced to.
  23160. *
  23161. * @private
  23162. * @function Highcharts.Tooltip#cleanSplit
  23163. *
  23164. * @param {boolean} [force]
  23165. * Force destroy all tooltips.
  23166. */
  23167. cleanSplit(force) {
  23168. this.chart.series.forEach(function (series) {
  23169. const tt = series && series.tt;
  23170. if (tt) {
  23171. if (!tt.isActive || force) {
  23172. series.tt = tt.destroy();
  23173. }
  23174. else {
  23175. tt.isActive = false;
  23176. }
  23177. }
  23178. });
  23179. }
  23180. /**
  23181. * In case no user defined formatter is given, this will be used. Note that
  23182. * the context here is an object holding point, series, x, y etc.
  23183. *
  23184. * @function Highcharts.Tooltip#defaultFormatter
  23185. *
  23186. * @param {Highcharts.Tooltip} tooltip
  23187. *
  23188. * @return {string|Array<string>}
  23189. * Returns a string (single tooltip and shared)
  23190. * or an array of strings (split tooltip)
  23191. */
  23192. defaultFormatter(tooltip) {
  23193. const items = this.points || splat(this);
  23194. let s;
  23195. // Build the header
  23196. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  23197. // build the values
  23198. s = s.concat(tooltip.bodyFormatter(items));
  23199. // footer
  23200. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  23201. return s;
  23202. }
  23203. /**
  23204. * Removes and destroys the tooltip and its elements.
  23205. *
  23206. * @function Highcharts.Tooltip#destroy
  23207. */
  23208. destroy() {
  23209. // Destroy and clear local variables
  23210. if (this.label) {
  23211. this.label = this.label.destroy();
  23212. }
  23213. if (this.split) {
  23214. this.cleanSplit(true);
  23215. if (this.tt) {
  23216. this.tt = this.tt.destroy();
  23217. }
  23218. }
  23219. if (this.renderer) {
  23220. this.renderer = this.renderer.destroy();
  23221. discardElement(this.container);
  23222. }
  23223. U.clearTimeout(this.hideTimer);
  23224. U.clearTimeout(this.tooltipTimeout);
  23225. }
  23226. /**
  23227. * Extendable method to get the anchor position of the tooltip
  23228. * from a point or set of points
  23229. *
  23230. * @private
  23231. * @function Highcharts.Tooltip#getAnchor
  23232. */
  23233. getAnchor(points, mouseEvent) {
  23234. const chart = this.chart, pointer = chart.pointer, inverted = chart.inverted, plotTop = chart.plotTop, plotLeft = chart.plotLeft;
  23235. let ret;
  23236. points = splat(points);
  23237. // If reversedStacks are false the tooltip position should be taken from
  23238. // the last point (#17948)
  23239. if (points[0].series &&
  23240. points[0].series.yAxis &&
  23241. !points[0].series.yAxis.options.reversedStacks) {
  23242. points = points.slice().reverse();
  23243. }
  23244. // When tooltip follows mouse, relate the position to the mouse
  23245. if (this.followPointer && mouseEvent) {
  23246. if (typeof mouseEvent.chartX === 'undefined') {
  23247. mouseEvent = pointer.normalize(mouseEvent);
  23248. }
  23249. ret = [
  23250. mouseEvent.chartX - plotLeft,
  23251. mouseEvent.chartY - plotTop
  23252. ];
  23253. // Some series types use a specificly calculated tooltip position for
  23254. // each point
  23255. }
  23256. else if (points[0].tooltipPos) {
  23257. ret = points[0].tooltipPos;
  23258. // Calculate the average position and adjust for axis positions
  23259. }
  23260. else {
  23261. let chartX = 0, chartY = 0;
  23262. points.forEach(function (point) {
  23263. const pos = point.pos(true);
  23264. if (pos) {
  23265. chartX += pos[0];
  23266. chartY += pos[1];
  23267. }
  23268. });
  23269. chartX /= points.length;
  23270. chartY /= points.length;
  23271. // When shared, place the tooltip next to the mouse (#424)
  23272. if (this.shared && points.length > 1 && mouseEvent) {
  23273. if (inverted) {
  23274. chartX = mouseEvent.chartX;
  23275. }
  23276. else {
  23277. chartY = mouseEvent.chartY;
  23278. }
  23279. }
  23280. // Use the average position for multiple points
  23281. ret = [chartX - plotLeft, chartY - plotTop];
  23282. }
  23283. return ret.map(Math.round);
  23284. }
  23285. /**
  23286. * Get the CSS class names for the tooltip's label. Styles the label
  23287. * by `colorIndex` or user-defined CSS.
  23288. *
  23289. * @function Highcharts.Tooltip#getClassName
  23290. *
  23291. * @return {string}
  23292. * The class names.
  23293. */
  23294. getClassName(point, isSplit, isHeader) {
  23295. const options = this.options, series = point.series, seriesOptions = series.options;
  23296. return [
  23297. options.className,
  23298. 'highcharts-label',
  23299. isHeader && 'highcharts-tooltip-header',
  23300. isSplit ? 'highcharts-tooltip-box' : 'highcharts-tooltip',
  23301. !isHeader && 'highcharts-color-' + pick(point.colorIndex, series.colorIndex),
  23302. (seriesOptions && seriesOptions.className)
  23303. ].filter(isString).join(' ');
  23304. }
  23305. /**
  23306. * Creates the Tooltip label element if it does not exist, then returns it.
  23307. *
  23308. * @function Highcharts.Tooltip#getLabel
  23309. *
  23310. * @return {Highcharts.SVGElement}
  23311. * Tooltip label
  23312. */
  23313. getLabel() {
  23314. const tooltip = this, styledMode = this.chart.styledMode, options = this.options, doSplit = this.split && this.allowShared, pointerEvents = (options.style.pointerEvents ||
  23315. (this.shouldStickOnContact() ? 'auto' : 'none'));
  23316. let container, renderer = this.chart.renderer;
  23317. // If changing from a split tooltip to a non-split tooltip, we must
  23318. // destroy it in order to get the SVG right. #13868.
  23319. if (this.label) {
  23320. const wasSplit = !this.label.hasClass('highcharts-label');
  23321. if ((!doSplit && wasSplit) || (doSplit && !wasSplit)) {
  23322. this.destroy();
  23323. }
  23324. }
  23325. if (!this.label) {
  23326. if (this.outside) {
  23327. const chartStyle = this.chart.options.chart.style, Renderer = RendererRegistry.getRendererType();
  23328. /**
  23329. * Reference to the tooltip's container, when
  23330. * [Highcharts.Tooltip#outside] is set to true, otherwise
  23331. * it's undefined.
  23332. *
  23333. * @name Highcharts.Tooltip#container
  23334. * @type {Highcharts.HTMLDOMElement|undefined}
  23335. */
  23336. this.container = container = H.doc.createElement('div');
  23337. container.className = 'highcharts-tooltip-container';
  23338. css(container, {
  23339. position: 'absolute',
  23340. top: '1px',
  23341. pointerEvents,
  23342. zIndex: Math.max(this.options.style.zIndex || 0, (chartStyle && chartStyle.zIndex || 0) + 3)
  23343. });
  23344. H.doc.body.appendChild(container);
  23345. /**
  23346. * Reference to the tooltip's renderer, when
  23347. * [Highcharts.Tooltip#outside] is set to true, otherwise
  23348. * it's undefined.
  23349. *
  23350. * @name Highcharts.Tooltip#renderer
  23351. * @type {Highcharts.SVGRenderer|undefined}
  23352. */
  23353. this.renderer = renderer = new Renderer(container, 0, 0, chartStyle, void 0, void 0, renderer.styledMode);
  23354. }
  23355. // Create the label
  23356. if (doSplit) {
  23357. this.label = renderer.g('tooltip');
  23358. }
  23359. else {
  23360. this.label = renderer
  23361. .label('', 0, 0, options.shape, void 0, void 0, options.useHTML, void 0, 'tooltip')
  23362. .attr({
  23363. padding: options.padding,
  23364. r: options.borderRadius
  23365. });
  23366. if (!styledMode) {
  23367. this.label
  23368. .attr({
  23369. fill: options.backgroundColor,
  23370. 'stroke-width': options.borderWidth || 0
  23371. })
  23372. // #2301, #2657
  23373. .css(options.style)
  23374. .css({ pointerEvents });
  23375. }
  23376. }
  23377. // Split tooltip use updateTooltipContainer to position the tooltip
  23378. // container.
  23379. if (tooltip.outside) {
  23380. const label = this.label;
  23381. const { xSetter, ySetter } = label;
  23382. label.xSetter = function (value) {
  23383. xSetter.call(label, tooltip.distance);
  23384. container.style.left = value + 'px';
  23385. };
  23386. label.ySetter = function (value) {
  23387. ySetter.call(label, tooltip.distance);
  23388. container.style.top = value + 'px';
  23389. };
  23390. }
  23391. this.label
  23392. .attr({ zIndex: 8 })
  23393. .shadow(options.shadow)
  23394. .add();
  23395. }
  23396. return this.label;
  23397. }
  23398. /**
  23399. * Get the total area available area to place the tooltip
  23400. *
  23401. * @private
  23402. */
  23403. getPlayingField() {
  23404. const { body, documentElement } = doc, { chart, distance, outside } = this;
  23405. return {
  23406. width: outside ?
  23407. // Substract distance to prevent scrollbars
  23408. Math.max(body.scrollWidth, documentElement.scrollWidth, body.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth) - 2 * distance :
  23409. chart.chartWidth,
  23410. height: outside ?
  23411. Math.max(body.scrollHeight, documentElement.scrollHeight, body.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight) :
  23412. chart.chartHeight
  23413. };
  23414. }
  23415. /**
  23416. * Place the tooltip in a chart without spilling over and not covering the
  23417. * point itself.
  23418. *
  23419. * @function Highcharts.Tooltip#getPosition
  23420. *
  23421. * @param {number} boxWidth
  23422. * Width of the tooltip box.
  23423. *
  23424. * @param {number} boxHeight
  23425. * Height of the tooltip box.
  23426. *
  23427. * @param {Highcharts.Point} point
  23428. * Tooltip related point.
  23429. *
  23430. * @return {Highcharts.PositionObject}
  23431. * Recommended position of the tooltip.
  23432. */
  23433. getPosition(boxWidth, boxHeight, point) {
  23434. const chart = this.chart, distance = this.distance, ret = {},
  23435. // Don't use h if chart isn't inverted (#7242) ???
  23436. h = (chart.inverted && point.h) || 0, // #4117 ???
  23437. outside = this.outside, playingField = this.getPlayingField(), outerWidth = playingField.width, outerHeight = playingField.height, chartPosition = chart.pointer.getChartPosition(), scaleX = (val) => ( // eslint-disable-line no-confusing-arrow
  23438. val * chartPosition.scaleX), scaleY = (val) => ( // eslint-disable-line no-confusing-arrow
  23439. val * chartPosition.scaleY),
  23440. // Build parameter arrays for firstDimension()/secondDimension()
  23441. buildDimensionArray = (dim) => {
  23442. const isX = dim === 'x';
  23443. return [
  23444. dim,
  23445. isX ? outerWidth : outerHeight,
  23446. isX ? boxWidth : boxHeight
  23447. ].concat(outside ? [
  23448. // If we are using tooltip.outside, we need to scale the
  23449. // position to match scaling of the container in case there
  23450. // is a transform/zoom on the container. #11329
  23451. isX ? scaleX(boxWidth) : scaleY(boxHeight),
  23452. isX ? chartPosition.left - distance +
  23453. scaleX(point.plotX + chart.plotLeft) :
  23454. chartPosition.top - distance +
  23455. scaleY(point.plotY + chart.plotTop),
  23456. 0,
  23457. isX ? outerWidth : outerHeight
  23458. ] : [
  23459. // Not outside, no scaling is needed
  23460. isX ? boxWidth : boxHeight,
  23461. isX ? point.plotX + chart.plotLeft :
  23462. point.plotY + chart.plotTop,
  23463. isX ? chart.plotLeft : chart.plotTop,
  23464. isX ? chart.plotLeft + chart.plotWidth :
  23465. chart.plotTop + chart.plotHeight
  23466. ]);
  23467. };
  23468. let first = buildDimensionArray('y'), second = buildDimensionArray('x'), swapped;
  23469. // Handle negative points or reversed axis (#13780)
  23470. let flipped = !!point.negative;
  23471. if (!chart.polar &&
  23472. chart.hoverSeries &&
  23473. chart.hoverSeries.yAxis &&
  23474. chart.hoverSeries.yAxis.reversed) {
  23475. flipped = !flipped;
  23476. }
  23477. // The far side is right or bottom
  23478. const preferFarSide = !this.followPointer &&
  23479. pick(point.ttBelow, !chart.inverted === flipped), // #4984
  23480. /*
  23481. * Handle the preferred dimension. When the preferred dimension is
  23482. * tooltip on top or bottom of the point, it will look for space
  23483. * there.
  23484. *
  23485. * @private
  23486. */
  23487. firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  23488. point, min, max) {
  23489. const scaledDist = outside ?
  23490. (dim === 'y' ? scaleY(distance) : scaleX(distance)) :
  23491. distance, scaleDiff = (innerSize - scaledInnerSize) / 2, roomLeft = scaledInnerSize < point - distance, roomRight = point + distance + scaledInnerSize < outerSize, alignedLeft = point - scaledDist - innerSize + scaleDiff, alignedRight = point + scaledDist - scaleDiff;
  23492. if (preferFarSide && roomRight) {
  23493. ret[dim] = alignedRight;
  23494. }
  23495. else if (!preferFarSide && roomLeft) {
  23496. ret[dim] = alignedLeft;
  23497. }
  23498. else if (roomLeft) {
  23499. ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
  23500. }
  23501. else if (roomRight) {
  23502. ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
  23503. alignedRight :
  23504. alignedRight + h);
  23505. }
  23506. else {
  23507. return false;
  23508. }
  23509. },
  23510. /*
  23511. * Handle the secondary dimension. If the preferred dimension is
  23512. * tooltip on top or bottom of the point, the second dimension is to
  23513. * align the tooltip above the point, trying to align center but
  23514. * allowing left or right align within the chart box.
  23515. *
  23516. * @private
  23517. */
  23518. secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  23519. point) {
  23520. let retVal;
  23521. // Too close to the edge, return false and swap dimensions
  23522. if (point < distance || point > outerSize - distance) {
  23523. retVal = false;
  23524. // Align left/top
  23525. }
  23526. else if (point < innerSize / 2) {
  23527. ret[dim] = 1;
  23528. // Align right/bottom
  23529. }
  23530. else if (point > outerSize - scaledInnerSize / 2) {
  23531. ret[dim] = outerSize - scaledInnerSize - 2;
  23532. // Align center
  23533. }
  23534. else {
  23535. ret[dim] = point - innerSize / 2;
  23536. }
  23537. return retVal;
  23538. },
  23539. /*
  23540. * Swap the dimensions
  23541. */
  23542. swap = function (count) {
  23543. const temp = first;
  23544. first = second;
  23545. second = temp;
  23546. swapped = count;
  23547. }, run = function () {
  23548. if (firstDimension.apply(0, first) !== false) {
  23549. if (secondDimension.apply(0, second) === false &&
  23550. !swapped) {
  23551. swap(true);
  23552. run();
  23553. }
  23554. }
  23555. else if (!swapped) {
  23556. swap(true);
  23557. run();
  23558. }
  23559. else {
  23560. ret.x = ret.y = 0;
  23561. }
  23562. };
  23563. // Under these conditions, prefer the tooltip on the side of the point
  23564. if (chart.inverted || this.len > 1) {
  23565. swap();
  23566. }
  23567. run();
  23568. return ret;
  23569. }
  23570. /**
  23571. * Hides the tooltip with a fade out animation.
  23572. *
  23573. * @function Highcharts.Tooltip#hide
  23574. *
  23575. * @param {number} [delay]
  23576. * The fade out in milliseconds. If no value is provided the value
  23577. * of the tooltip.hideDelay option is used. A value of 0 disables
  23578. * the fade out animation.
  23579. */
  23580. hide(delay) {
  23581. const tooltip = this;
  23582. // disallow duplicate timers (#1728, #1766)
  23583. U.clearTimeout(this.hideTimer);
  23584. delay = pick(delay, this.options.hideDelay);
  23585. if (!this.isHidden) {
  23586. this.hideTimer = syncTimeout(function () {
  23587. // If there is a delay, do fadeOut with the default duration. If
  23588. // the hideDelay is 0, we assume no animation is wanted, so we
  23589. // pass 0 duration. #12994.
  23590. tooltip.getLabel().fadeOut(delay ? void 0 : delay);
  23591. tooltip.isHidden = true;
  23592. }, delay);
  23593. }
  23594. }
  23595. /**
  23596. * Initialize tooltip.
  23597. *
  23598. * @private
  23599. * @function Highcharts.Tooltip#init
  23600. *
  23601. * @param {Highcharts.Chart} chart
  23602. * The chart instance.
  23603. *
  23604. * @param {Highcharts.TooltipOptions} options
  23605. * Tooltip options.
  23606. */
  23607. init(chart, options) {
  23608. /**
  23609. * Chart of the tooltip.
  23610. *
  23611. * @readonly
  23612. * @name Highcharts.Tooltip#chart
  23613. * @type {Highcharts.Chart}
  23614. */
  23615. this.chart = chart;
  23616. /**
  23617. * Used tooltip options.
  23618. *
  23619. * @readonly
  23620. * @name Highcharts.Tooltip#options
  23621. * @type {Highcharts.TooltipOptions}
  23622. */
  23623. this.options = options;
  23624. /**
  23625. * List of crosshairs.
  23626. *
  23627. * @private
  23628. * @readonly
  23629. * @name Highcharts.Tooltip#crosshairs
  23630. * @type {Array<null>}
  23631. */
  23632. this.crosshairs = [];
  23633. /**
  23634. * Current values of x and y when animating.
  23635. *
  23636. * @private
  23637. * @readonly
  23638. * @name Highcharts.Tooltip#now
  23639. * @type {Highcharts.PositionObject}
  23640. */
  23641. this.now = { x: 0, y: 0 };
  23642. /**
  23643. * Tooltips are initially hidden.
  23644. *
  23645. * @private
  23646. * @readonly
  23647. * @name Highcharts.Tooltip#isHidden
  23648. * @type {boolean}
  23649. */
  23650. this.isHidden = true;
  23651. /**
  23652. * True, if the tooltip is split into one label per series, with the
  23653. * header close to the axis.
  23654. *
  23655. * @readonly
  23656. * @name Highcharts.Tooltip#split
  23657. * @type {boolean|undefined}
  23658. */
  23659. this.split = options.split && !chart.inverted && !chart.polar;
  23660. /**
  23661. * When the tooltip is shared, the entire plot area will capture mouse
  23662. * movement or touch events.
  23663. *
  23664. * @readonly
  23665. * @name Highcharts.Tooltip#shared
  23666. * @type {boolean|undefined}
  23667. */
  23668. this.shared = options.shared || this.split;
  23669. /**
  23670. * Whether to allow the tooltip to render outside the chart's SVG
  23671. * element box. By default (false), the tooltip is rendered within the
  23672. * chart's SVG element, which results in the tooltip being aligned
  23673. * inside the chart area.
  23674. *
  23675. * @readonly
  23676. * @name Highcharts.Tooltip#outside
  23677. * @type {boolean}
  23678. *
  23679. * @todo
  23680. * Split tooltip does not support outside in the first iteration. Should
  23681. * not be too complicated to implement.
  23682. */
  23683. this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
  23684. }
  23685. shouldStickOnContact(pointerEvent) {
  23686. return !!(!this.followPointer &&
  23687. this.options.stickOnContact &&
  23688. (!pointerEvent || this.chart.pointer.inClass(pointerEvent.target, 'highcharts-tooltip')));
  23689. }
  23690. /**
  23691. * Moves the tooltip with a soft animation to a new position.
  23692. *
  23693. * @private
  23694. * @function Highcharts.Tooltip#move
  23695. *
  23696. * @param {number} x
  23697. *
  23698. * @param {number} y
  23699. *
  23700. * @param {number} anchorX
  23701. *
  23702. * @param {number} anchorY
  23703. */
  23704. move(x, y, anchorX, anchorY) {
  23705. const tooltip = this, now = tooltip.now, animate = tooltip.options.animation !== false &&
  23706. !tooltip.isHidden &&
  23707. // When we get close to the target position, abort animation and
  23708. // land on the right place (#3056)
  23709. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1), skipAnchor = tooltip.followPointer || tooltip.len > 1;
  23710. // Get intermediate values for animation
  23711. extend(now, {
  23712. x: animate ? (2 * now.x + x) / 3 : x,
  23713. y: animate ? (now.y + y) / 2 : y,
  23714. anchorX: skipAnchor ?
  23715. void 0 :
  23716. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  23717. anchorY: skipAnchor ?
  23718. void 0 :
  23719. animate ? (now.anchorY + anchorY) / 2 : anchorY
  23720. });
  23721. // Move to the intermediate value
  23722. tooltip.getLabel().attr(now);
  23723. tooltip.drawTracker();
  23724. // Run on next tick of the mouse tracker
  23725. if (animate) {
  23726. // Never allow two timeouts
  23727. U.clearTimeout(this.tooltipTimeout);
  23728. // Set the fixed interval ticking for the smooth tooltip
  23729. this.tooltipTimeout = setTimeout(function () {
  23730. // The interval function may still be running during destroy,
  23731. // so check that the chart is really there before calling.
  23732. if (tooltip) {
  23733. tooltip.move(x, y, anchorX, anchorY);
  23734. }
  23735. }, 32);
  23736. }
  23737. }
  23738. /**
  23739. * Refresh the tooltip's text and position.
  23740. *
  23741. * @function Highcharts.Tooltip#refresh
  23742. *
  23743. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  23744. * Either a point or an array of points.
  23745. *
  23746. * @param {Highcharts.PointerEventObject} [mouseEvent]
  23747. * Mouse event, that is responsible for the refresh and should be
  23748. * used for the tooltip update.
  23749. */
  23750. refresh(pointOrPoints, mouseEvent) {
  23751. const tooltip = this, chart = this.chart, options = tooltip.options, pointer = chart.pointer, points = splat(pointOrPoints), point = points[0], pointConfig = [], formatString = options.format, formatter = options.formatter || tooltip.defaultFormatter, shared = tooltip.shared, styledMode = chart.styledMode;
  23752. let formatterContext = {};
  23753. if (!options.enabled || !point.series) { // #16820
  23754. return;
  23755. }
  23756. U.clearTimeout(this.hideTimer);
  23757. // A switch saying if this specific tooltip configuration allows shared
  23758. // or split modes
  23759. tooltip.allowShared = !(!isArray(pointOrPoints) &&
  23760. pointOrPoints.series &&
  23761. pointOrPoints.series.noSharedTooltip);
  23762. // get the reference point coordinates (pie charts use tooltipPos)
  23763. tooltip.followPointer = (!tooltip.split && point.series.tooltipOptions.followPointer);
  23764. const anchor = tooltip.getAnchor(pointOrPoints, mouseEvent), x = anchor[0], y = anchor[1];
  23765. // shared tooltip, array is sent over
  23766. if (shared && tooltip.allowShared) {
  23767. pointer.applyInactiveState(points);
  23768. // Now set hover state for the choosen ones:
  23769. points.forEach(function (item) {
  23770. item.setState('hover');
  23771. pointConfig.push(item.getLabelConfig());
  23772. });
  23773. formatterContext = point.getLabelConfig();
  23774. formatterContext.points = pointConfig;
  23775. // single point tooltip
  23776. }
  23777. else {
  23778. formatterContext = point.getLabelConfig();
  23779. }
  23780. this.len = pointConfig.length; // #6128
  23781. const text = isString(formatString) ?
  23782. format(formatString, formatterContext, chart) :
  23783. formatter.call(formatterContext, tooltip);
  23784. // register the current series
  23785. const currentSeries = point.series;
  23786. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  23787. // update the inner HTML
  23788. if (text === false) {
  23789. this.hide();
  23790. }
  23791. else {
  23792. // update text
  23793. if (tooltip.split && tooltip.allowShared) { // #13868
  23794. this.renderSplit(text, points);
  23795. }
  23796. else {
  23797. let checkX = x;
  23798. let checkY = y;
  23799. if (mouseEvent && pointer.isDirectTouch) {
  23800. checkX = mouseEvent.chartX - chart.plotLeft;
  23801. checkY = mouseEvent.chartY - chart.plotTop;
  23802. }
  23803. // #11493, #13095
  23804. if (chart.polar ||
  23805. currentSeries.options.clip === false ||
  23806. points.some((p) => // #16004
  23807. pointer.isDirectTouch || // ##17929
  23808. p.series.shouldShowTooltip(checkX, checkY))) {
  23809. const label = tooltip.getLabel();
  23810. // Prevent the tooltip from flowing over the chart box
  23811. // (#6659)
  23812. if (!options.style.width || styledMode) {
  23813. label.css({
  23814. width: (this.outside ?
  23815. this.getPlayingField() :
  23816. chart.spacingBox).width + 'px'
  23817. });
  23818. }
  23819. label.attr({
  23820. text: text && text.join ?
  23821. text.join('') :
  23822. text
  23823. });
  23824. // Set the stroke color of the box to reflect the point
  23825. label.addClass(tooltip.getClassName(point), true);
  23826. if (!styledMode) {
  23827. label.attr({
  23828. stroke: (options.borderColor ||
  23829. point.color ||
  23830. currentSeries.color ||
  23831. "#666666" /* Palette.neutralColor60 */)
  23832. });
  23833. }
  23834. tooltip.updatePosition({
  23835. plotX: x,
  23836. plotY: y,
  23837. negative: point.negative,
  23838. ttBelow: point.ttBelow,
  23839. h: anchor[2] || 0
  23840. });
  23841. }
  23842. else {
  23843. tooltip.hide();
  23844. return;
  23845. }
  23846. }
  23847. // show it
  23848. if (tooltip.isHidden && tooltip.label) {
  23849. tooltip.label.attr({
  23850. opacity: 1
  23851. }).show();
  23852. }
  23853. tooltip.isHidden = false;
  23854. }
  23855. fireEvent(this, 'refresh');
  23856. }
  23857. /**
  23858. * Render the split tooltip. Loops over each point's text and adds
  23859. * a label next to the point, then uses the distribute function to
  23860. * find best non-overlapping positions.
  23861. *
  23862. * @private
  23863. * @function Highcharts.Tooltip#renderSplit
  23864. *
  23865. * @param {string|Array<(boolean|string)>} labels
  23866. *
  23867. * @param {Array<Highcharts.Point>} points
  23868. */
  23869. renderSplit(labels, points) {
  23870. const tooltip = this;
  23871. const { chart, chart: { chartWidth, chartHeight, plotHeight, plotLeft, plotTop, pointer, scrollablePixelsY = 0, scrollablePixelsX, scrollingContainer: { scrollLeft, scrollTop } = { scrollLeft: 0, scrollTop: 0 }, styledMode }, distance, options, options: { positioner } } = tooltip;
  23872. // The area which the tooltip should be limited to. Limit to scrollable
  23873. // plot area if enabled, otherwise limit to the chart container. If
  23874. // outside is true it should be the whole viewport
  23875. const bounds = (tooltip.outside &&
  23876. typeof scrollablePixelsX !== 'number') ?
  23877. doc.documentElement.getBoundingClientRect() : {
  23878. left: scrollLeft,
  23879. right: scrollLeft + chartWidth,
  23880. top: scrollTop,
  23881. bottom: scrollTop + chartHeight
  23882. };
  23883. const tooltipLabel = tooltip.getLabel();
  23884. const ren = this.renderer || chart.renderer;
  23885. const headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
  23886. const { left: chartLeft, top: chartTop } = pointer.getChartPosition();
  23887. let distributionBoxTop = plotTop + scrollTop;
  23888. let headerHeight = 0;
  23889. let adjustedPlotHeight = plotHeight - scrollablePixelsY;
  23890. /**
  23891. * Calculates the anchor position for the partial tooltip
  23892. *
  23893. * @private
  23894. * @param {Highcharts.Point} point The point related to the tooltip
  23895. * @return {Object} Returns an object with anchorX and anchorY
  23896. */
  23897. function getAnchor(point) {
  23898. const { isHeader, plotX = 0, plotY = 0, series } = point;
  23899. let anchorX;
  23900. let anchorY;
  23901. if (isHeader) {
  23902. // Set anchorX to plotX
  23903. anchorX = plotLeft + plotX;
  23904. // Set anchorY to center of visible plot area.
  23905. anchorY = plotTop + plotHeight / 2;
  23906. }
  23907. else {
  23908. const { xAxis, yAxis } = series;
  23909. // Set anchorX to plotX. Limit to within xAxis.
  23910. anchorX = xAxis.pos + clamp(plotX, -distance, xAxis.len + distance);
  23911. // Set anchorY, limit to the scrollable plot area
  23912. if (series.shouldShowTooltip(0, yAxis.pos - plotTop + plotY, {
  23913. ignoreX: true
  23914. })) {
  23915. anchorY = yAxis.pos + plotY;
  23916. }
  23917. }
  23918. // Limit values to plot area
  23919. anchorX = clamp(anchorX, bounds.left - distance, bounds.right + distance);
  23920. return { anchorX, anchorY };
  23921. }
  23922. /**
  23923. * Calculates the position of the partial tooltip
  23924. *
  23925. * @private
  23926. * @param {number} anchorX
  23927. * The partial tooltip anchor x position
  23928. *
  23929. * @param {number} anchorY
  23930. * The partial tooltip anchor y position
  23931. *
  23932. * @param {boolean|undefined} isHeader
  23933. * Whether the partial tooltip is a header
  23934. *
  23935. * @param {number} boxWidth
  23936. * Width of the partial tooltip
  23937. *
  23938. * @return {Highcharts.PositionObject}
  23939. * Returns the partial tooltip x and y position
  23940. */
  23941. function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft = true) {
  23942. let y;
  23943. let x;
  23944. if (isHeader) {
  23945. y = headerTop ? 0 : adjustedPlotHeight;
  23946. x = clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth - (tooltip.outside ? chartLeft : 0));
  23947. }
  23948. else {
  23949. y = anchorY - distributionBoxTop;
  23950. x = alignedLeft ?
  23951. anchorX - boxWidth - distance :
  23952. anchorX + distance;
  23953. x = clamp(x, alignedLeft ? x : bounds.left, bounds.right);
  23954. }
  23955. // NOTE: y is relative to distributionBoxTop
  23956. return { x, y };
  23957. }
  23958. /**
  23959. * Updates the attributes and styling of the partial tooltip. Creates a
  23960. * new partial tooltip if it does not exists.
  23961. *
  23962. * @private
  23963. * @param {Highcharts.SVGElement|undefined} partialTooltip
  23964. * The partial tooltip to update
  23965. * @param {Highcharts.Point} point
  23966. * The point related to the partial tooltip
  23967. * @param {boolean|string} str The text for the partial tooltip
  23968. * @return {Highcharts.SVGElement} Returns the updated partial tooltip
  23969. */
  23970. function updatePartialTooltip(partialTooltip, point, str) {
  23971. var _a;
  23972. let tt = partialTooltip;
  23973. const { isHeader, series } = point;
  23974. if (!tt) {
  23975. const attribs = {
  23976. padding: options.padding,
  23977. r: options.borderRadius
  23978. };
  23979. if (!styledMode) {
  23980. attribs.fill = options.backgroundColor;
  23981. attribs['stroke-width'] = (_a = options.borderWidth) !== null && _a !== void 0 ? _a : 1;
  23982. }
  23983. tt = ren
  23984. .label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']), void 0, void 0, options.useHTML)
  23985. .addClass(tooltip.getClassName(point, true, isHeader))
  23986. .attr(attribs)
  23987. .add(tooltipLabel);
  23988. }
  23989. tt.isActive = true;
  23990. tt.attr({
  23991. text: str
  23992. });
  23993. if (!styledMode) {
  23994. tt.css(options.style)
  23995. .attr({
  23996. stroke: (options.borderColor ||
  23997. point.color ||
  23998. series.color ||
  23999. "#333333" /* Palette.neutralColor80 */)
  24000. });
  24001. }
  24002. return tt;
  24003. }
  24004. // Graceful degradation for legacy formatters
  24005. if (isString(labels)) {
  24006. labels = [false, labels];
  24007. }
  24008. // Create the individual labels for header and points, ignore footer
  24009. let boxes = labels.slice(0, points.length + 1).reduce(function (boxes, str, i) {
  24010. if (str !== false && str !== '') {
  24011. const point = (points[i - 1] ||
  24012. {
  24013. // Item 0 is the header. Instead of this, we could also
  24014. // use the crosshair label
  24015. isHeader: true,
  24016. plotX: points[0].plotX,
  24017. plotY: plotHeight,
  24018. series: {}
  24019. });
  24020. const isHeader = point.isHeader;
  24021. // Store the tooltip label referance on the series
  24022. const owner = isHeader ? tooltip : point.series;
  24023. const tt = owner.tt = updatePartialTooltip(owner.tt, point, str.toString());
  24024. // Get X position now, so we can move all to the other side in
  24025. // case of overflow
  24026. const bBox = tt.getBBox();
  24027. const boxWidth = bBox.width + tt.strokeWidth();
  24028. if (isHeader) {
  24029. headerHeight = bBox.height;
  24030. adjustedPlotHeight += headerHeight;
  24031. if (headerTop) {
  24032. distributionBoxTop -= headerHeight;
  24033. }
  24034. }
  24035. const { anchorX, anchorY } = getAnchor(point);
  24036. if (typeof anchorY === 'number') {
  24037. const size = bBox.height + 1;
  24038. const boxPosition = (positioner ?
  24039. positioner.call(tooltip, boxWidth, size, point) :
  24040. defaultPositioner(anchorX, anchorY, isHeader, boxWidth));
  24041. boxes.push({
  24042. // 0-align to the top, 1-align to the bottom
  24043. align: positioner ? 0 : void 0,
  24044. anchorX,
  24045. anchorY,
  24046. boxWidth,
  24047. point,
  24048. rank: pick(boxPosition.rank, isHeader ? 1 : 0),
  24049. size,
  24050. target: boxPosition.y,
  24051. tt,
  24052. x: boxPosition.x
  24053. });
  24054. }
  24055. else {
  24056. // Hide tooltips which anchorY is outside the visible plot
  24057. // area
  24058. tt.isActive = false;
  24059. }
  24060. }
  24061. return boxes;
  24062. }, []);
  24063. // Realign the tooltips towards the right if there is not enough space
  24064. // to the left and there is space to to the right
  24065. if (!positioner && boxes.some((box) => {
  24066. // Always realign if the beginning of a label is outside bounds
  24067. const { outside } = tooltip;
  24068. const boxStart = (outside ? chartLeft : 0) + box.anchorX;
  24069. if (boxStart < bounds.left &&
  24070. boxStart + box.boxWidth < bounds.right) {
  24071. return true;
  24072. }
  24073. // Otherwise, check if there is more space available to the right
  24074. return boxStart < (chartLeft - bounds.left) + box.boxWidth &&
  24075. bounds.right - boxStart > boxStart;
  24076. })) {
  24077. boxes = boxes.map((box) => {
  24078. const { x, y } = defaultPositioner(box.anchorX, box.anchorY, box.point.isHeader, box.boxWidth, false);
  24079. return extend(box, {
  24080. target: y,
  24081. x
  24082. });
  24083. });
  24084. }
  24085. // Clean previous run (for missing points)
  24086. tooltip.cleanSplit();
  24087. // Distribute and put in place
  24088. distribute(boxes, adjustedPlotHeight);
  24089. const boxExtremes = {
  24090. left: chartLeft,
  24091. right: chartLeft
  24092. };
  24093. // Get the extremes from series tooltips
  24094. boxes.forEach(function (box) {
  24095. const { x, boxWidth, isHeader } = box;
  24096. if (!isHeader) {
  24097. if (tooltip.outside && chartLeft + x < boxExtremes.left) {
  24098. boxExtremes.left = chartLeft + x;
  24099. }
  24100. if (!isHeader &&
  24101. tooltip.outside &&
  24102. boxExtremes.left + boxWidth > boxExtremes.right) {
  24103. boxExtremes.right = chartLeft + x;
  24104. }
  24105. }
  24106. });
  24107. boxes.forEach(function (box) {
  24108. const { x, anchorX, anchorY, pos, point: { isHeader } } = box;
  24109. const attributes = {
  24110. visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
  24111. x,
  24112. /* NOTE: y should equal pos to be consistent with !split
  24113. * tooltip, but is currently relative to plotTop. Is left as is
  24114. * to avoid breaking change. Remove distributionBoxTop to make
  24115. * it consistent.
  24116. */
  24117. y: (pos || 0) + distributionBoxTop,
  24118. anchorX,
  24119. anchorY
  24120. };
  24121. // Handle left-aligned tooltips overflowing the chart area
  24122. if (tooltip.outside && x < anchorX) {
  24123. const offset = chartLeft - boxExtremes.left;
  24124. // Skip this if there is no overflow
  24125. if (offset > 0) {
  24126. if (!isHeader) {
  24127. attributes.x = x + offset;
  24128. attributes.anchorX = anchorX + offset;
  24129. }
  24130. if (isHeader) {
  24131. attributes.x = (boxExtremes.right - boxExtremes.left) / 2;
  24132. attributes.anchorX = anchorX + offset;
  24133. }
  24134. }
  24135. }
  24136. // Put the label in place
  24137. box.tt.attr(attributes);
  24138. });
  24139. /* If we have a seperate tooltip container, then update the necessary
  24140. * container properties.
  24141. * Test that tooltip has its own container and renderer before executing
  24142. * the operation.
  24143. */
  24144. const { container, outside, renderer } = tooltip;
  24145. if (outside && container && renderer) {
  24146. // Set container size to fit the bounds
  24147. const { width, height, x, y } = tooltipLabel.getBBox();
  24148. renderer.setSize(width + x, height + y, false);
  24149. // Position the tooltip container to the chart container
  24150. container.style.left = boxExtremes.left + 'px';
  24151. container.style.top = chartTop + 'px';
  24152. }
  24153. // Workaround for #18927, artefacts left by the shadows of split
  24154. // tooltips in Safari v16 (2023). Check again with later versions if we
  24155. // can remove this.
  24156. if (isSafari) {
  24157. tooltipLabel.attr({
  24158. // Force a redraw of the whole group by chaning the opacity
  24159. // slightly
  24160. opacity: tooltipLabel.opacity === 1 ? 0.999 : 1
  24161. });
  24162. }
  24163. }
  24164. /**
  24165. * If the `stickOnContact` option is active, this will add a tracker shape.
  24166. *
  24167. * @private
  24168. * @function Highcharts.Tooltip#drawTracker
  24169. */
  24170. drawTracker() {
  24171. const tooltip = this;
  24172. if (!this.shouldStickOnContact()) {
  24173. if (tooltip.tracker) {
  24174. tooltip.tracker = tooltip.tracker.destroy();
  24175. }
  24176. return;
  24177. }
  24178. const chart = tooltip.chart;
  24179. const label = tooltip.label;
  24180. const points = tooltip.shared ? chart.hoverPoints : chart.hoverPoint;
  24181. if (!label || !points) {
  24182. return;
  24183. }
  24184. const box = {
  24185. x: 0,
  24186. y: 0,
  24187. width: 0,
  24188. height: 0
  24189. };
  24190. // Combine anchor and tooltip
  24191. const anchorPos = this.getAnchor(points);
  24192. const labelBBox = label.getBBox();
  24193. anchorPos[0] += chart.plotLeft - label.translateX;
  24194. anchorPos[1] += chart.plotTop - label.translateY;
  24195. // When the mouse pointer is between the anchor point and the label,
  24196. // the label should stick.
  24197. box.x = Math.min(0, anchorPos[0]);
  24198. box.y = Math.min(0, anchorPos[1]);
  24199. box.width = (anchorPos[0] < 0 ?
  24200. Math.max(Math.abs(anchorPos[0]), (labelBBox.width - anchorPos[0])) :
  24201. Math.max(Math.abs(anchorPos[0]), labelBBox.width));
  24202. box.height = (anchorPos[1] < 0 ?
  24203. Math.max(Math.abs(anchorPos[1]), (labelBBox.height - Math.abs(anchorPos[1]))) :
  24204. Math.max(Math.abs(anchorPos[1]), labelBBox.height));
  24205. if (tooltip.tracker) {
  24206. tooltip.tracker.attr(box);
  24207. }
  24208. else {
  24209. tooltip.tracker = label.renderer
  24210. .rect(box)
  24211. .addClass('highcharts-tracker')
  24212. .add(label);
  24213. if (!chart.styledMode) {
  24214. tooltip.tracker.attr({
  24215. fill: 'rgba(0,0,0,0)'
  24216. });
  24217. }
  24218. }
  24219. }
  24220. /**
  24221. * @private
  24222. */
  24223. styledModeFormat(formatString) {
  24224. return formatString
  24225. .replace('style="font-size: 0.8em"', 'class="highcharts-header"')
  24226. .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex} ' +
  24227. '{series.options.className} ' +
  24228. '{point.options.className}"');
  24229. }
  24230. /**
  24231. * Format the footer/header of the tooltip
  24232. * #3397: abstraction to enable formatting of footer and header
  24233. *
  24234. * @private
  24235. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  24236. */
  24237. tooltipFooterHeaderFormatter(labelConfig, isFooter) {
  24238. const series = labelConfig.series, tooltipOptions = series.tooltipOptions, xAxis = series.xAxis, dateTime = xAxis && xAxis.dateTime, e = {
  24239. isFooter: isFooter,
  24240. labelConfig: labelConfig
  24241. };
  24242. let xDateFormat = tooltipOptions.xDateFormat, formatString = tooltipOptions[isFooter ? 'footerFormat' : 'headerFormat'];
  24243. fireEvent(this, 'headerFormatter', e, function (e) {
  24244. // Guess the best date format based on the closest point distance
  24245. // (#568, #3418)
  24246. if (dateTime && !xDateFormat && isNumber(labelConfig.key)) {
  24247. xDateFormat = dateTime.getXDateFormat(labelConfig.key, tooltipOptions.dateTimeLabelFormats);
  24248. }
  24249. // Insert the footer date format if any
  24250. if (dateTime && xDateFormat) {
  24251. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  24252. ['key']).forEach(function (key) {
  24253. formatString = formatString.replace('{point.' + key + '}', '{point.' + key + ':' + xDateFormat + '}');
  24254. });
  24255. }
  24256. // Replace default header style with class name
  24257. if (series.chart.styledMode) {
  24258. formatString = this.styledModeFormat(formatString);
  24259. }
  24260. e.text = format(formatString, {
  24261. point: labelConfig,
  24262. series: series
  24263. }, this.chart);
  24264. });
  24265. return e.text;
  24266. }
  24267. /**
  24268. * Updates the tooltip with the provided tooltip options.
  24269. *
  24270. * @function Highcharts.Tooltip#update
  24271. *
  24272. * @param {Highcharts.TooltipOptions} options
  24273. * The tooltip options to update.
  24274. */
  24275. update(options) {
  24276. this.destroy();
  24277. this.init(this.chart, merge(true, this.options, options));
  24278. }
  24279. /**
  24280. * Find the new position and perform the move
  24281. *
  24282. * @private
  24283. * @function Highcharts.Tooltip#updatePosition
  24284. *
  24285. * @param {Highcharts.Point} point
  24286. */
  24287. updatePosition(point) {
  24288. const { chart, distance, options } = this, pointer = chart.pointer, label = this.getLabel(),
  24289. // Needed for outside: true (#11688)
  24290. { left, top, scaleX, scaleY } = pointer.getChartPosition(), pos = (options.positioner || this.getPosition).call(this, label.width, label.height, point);
  24291. let anchorX = (point.plotX || 0) + chart.plotLeft, anchorY = (point.plotY || 0) + chart.plotTop, pad;
  24292. // Set the renderer size dynamically to prevent document size to change
  24293. if (this.outside) {
  24294. // Corrects positions, occurs with tooltip positioner (#16944)
  24295. if (options.positioner) {
  24296. pos.x += left - distance;
  24297. pos.y += top - distance;
  24298. }
  24299. pad = (options.borderWidth || 0) + 2 * distance;
  24300. this.renderer.setSize(label.width + pad, label.height + pad, false);
  24301. // Anchor and tooltip container need scaling if chart container has
  24302. // scale transform/css zoom. #11329.
  24303. if (scaleX !== 1 || scaleY !== 1) {
  24304. css(this.container, {
  24305. transform: `scale(${scaleX}, ${scaleY})`
  24306. });
  24307. anchorX *= scaleX;
  24308. anchorY *= scaleY;
  24309. }
  24310. anchorX += left - pos.x;
  24311. anchorY += top - pos.y;
  24312. }
  24313. // do the move
  24314. this.move(Math.round(pos.x), Math.round(pos.y || 0), // can be undefined (#3977)
  24315. anchorX, anchorY);
  24316. }
  24317. }
  24318. /* *
  24319. *
  24320. * Class namespace
  24321. *
  24322. * */
  24323. (function (Tooltip) {
  24324. /* *
  24325. *
  24326. * Declarations
  24327. *
  24328. * */
  24329. /* *
  24330. *
  24331. * Constants
  24332. *
  24333. * */
  24334. const composedMembers = [];
  24335. /* *
  24336. *
  24337. * Functions
  24338. *
  24339. * */
  24340. /**
  24341. * @private
  24342. */
  24343. function compose(PointerClass) {
  24344. if (U.pushUnique(composedMembers, PointerClass)) {
  24345. addEvent(PointerClass, 'afterInit', function () {
  24346. const chart = this.chart;
  24347. if (chart.options.tooltip) {
  24348. /**
  24349. * Tooltip object for points of series.
  24350. *
  24351. * @name Highcharts.Chart#tooltip
  24352. * @type {Highcharts.Tooltip}
  24353. */
  24354. chart.tooltip = new Tooltip(chart, chart.options.tooltip);
  24355. }
  24356. });
  24357. }
  24358. }
  24359. Tooltip.compose = compose;
  24360. })(Tooltip || (Tooltip = {}));
  24361. /* *
  24362. *
  24363. * Default export
  24364. *
  24365. * */
  24366. /* *
  24367. *
  24368. * API Declarations
  24369. *
  24370. * */
  24371. /**
  24372. * Callback function to format the text of the tooltip from scratch.
  24373. *
  24374. * In case of single or shared tooltips, a string should be be returned. In case
  24375. * of splitted tooltips, it should return an array where the first item is the
  24376. * header, and subsequent items are mapped to the points. Return `false` to
  24377. * disable tooltip for a specific point on series.
  24378. *
  24379. * @callback Highcharts.TooltipFormatterCallbackFunction
  24380. *
  24381. * @param {Highcharts.TooltipFormatterContextObject} this
  24382. * Context to format
  24383. *
  24384. * @param {Highcharts.Tooltip} tooltip
  24385. * The tooltip instance
  24386. *
  24387. * @return {false|string|Array<(string|null|undefined)>|null|undefined}
  24388. * Formatted text or false
  24389. */
  24390. /**
  24391. * Configuration for the tooltip formatters.
  24392. *
  24393. * @interface Highcharts.TooltipFormatterContextObject
  24394. * @extends Highcharts.PointLabelObject
  24395. */ /**
  24396. * Array of points in shared tooltips.
  24397. * @name Highcharts.TooltipFormatterContextObject#points
  24398. * @type {Array<Highcharts.TooltipFormatterContextObject>|undefined}
  24399. */
  24400. /**
  24401. * A callback function to place the tooltip in a specific position.
  24402. *
  24403. * @callback Highcharts.TooltipPositionerCallbackFunction
  24404. *
  24405. * @param {Highcharts.Tooltip} this
  24406. * Tooltip context of the callback.
  24407. *
  24408. * @param {number} labelWidth
  24409. * Width of the tooltip.
  24410. *
  24411. * @param {number} labelHeight
  24412. * Height of the tooltip.
  24413. *
  24414. * @param {Highcharts.TooltipPositionerPointObject} point
  24415. * Point information for positioning a tooltip.
  24416. *
  24417. * @return {Highcharts.PositionObject}
  24418. * New position for the tooltip.
  24419. */
  24420. /**
  24421. * Point information for positioning a tooltip.
  24422. *
  24423. * @interface Highcharts.TooltipPositionerPointObject
  24424. * @extends Highcharts.Point
  24425. */ /**
  24426. * If `tooltip.split` option is enabled and positioner is called for each of the
  24427. * boxes separately, this property indicates the call on the xAxis header, which
  24428. * is not a point itself.
  24429. * @name Highcharts.TooltipPositionerPointObject#isHeader
  24430. * @type {boolean}
  24431. */ /**
  24432. * The reference point relative to the plot area. Add chart.plotLeft to get the
  24433. * full coordinates.
  24434. * @name Highcharts.TooltipPositionerPointObject#plotX
  24435. * @type {number}
  24436. */ /**
  24437. * The reference point relative to the plot area. Add chart.plotTop to get the
  24438. * full coordinates.
  24439. * @name Highcharts.TooltipPositionerPointObject#plotY
  24440. * @type {number}
  24441. */
  24442. /**
  24443. * @typedef {"callout"|"circle"|"square"} Highcharts.TooltipShapeValue
  24444. */
  24445. ''; // keeps doclets above in JS file
  24446. return Tooltip;
  24447. });
  24448. _registerModule(_modules, 'Core/Series/Point.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Defaults.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (AST, A, D, F, U) {
  24449. /* *
  24450. *
  24451. * (c) 2010-2021 Torstein Honsi
  24452. *
  24453. * License: www.highcharts.com/license
  24454. *
  24455. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  24456. *
  24457. * */
  24458. const { animObject } = A;
  24459. const { defaultOptions } = D;
  24460. const { format } = F;
  24461. const { addEvent, defined, erase, extend, fireEvent, getNestedProperty, isArray, isFunction, isNumber, isObject, merge, objectEach, pick, syncTimeout, removeEvent, uniqueKey } = U;
  24462. /* eslint-disable no-invalid-this, valid-jsdoc */
  24463. /* *
  24464. *
  24465. * Class
  24466. *
  24467. * */
  24468. /**
  24469. * The Point object. The point objects are generated from the `series.data`
  24470. * configuration objects or raw numbers. They can be accessed from the
  24471. * `Series.points` array. Other ways to instantiate points are through {@link
  24472. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  24473. *
  24474. * @class
  24475. * @name Highcharts.Point
  24476. */
  24477. class Point {
  24478. constructor() {
  24479. /* *
  24480. *
  24481. * Properties
  24482. *
  24483. * */
  24484. /**
  24485. * For categorized axes this property holds the category name for the
  24486. * point. For other axes it holds the X value.
  24487. *
  24488. * @name Highcharts.Point#category
  24489. * @type {number|string}
  24490. */
  24491. this.category = void 0;
  24492. this.destroyed = false;
  24493. this.formatPrefix = 'point';
  24494. this.id = void 0;
  24495. this.isNull = false;
  24496. /**
  24497. * The name of the point. The name can be given as the first position of the
  24498. * point configuration array, or as a `name` property in the configuration:
  24499. *
  24500. * @example
  24501. * // Array config
  24502. * data: [
  24503. * ['John', 1],
  24504. * ['Jane', 2]
  24505. * ]
  24506. *
  24507. * // Object config
  24508. * data: [{
  24509. * name: 'John',
  24510. * y: 1
  24511. * }, {
  24512. * name: 'Jane',
  24513. * y: 2
  24514. * }]
  24515. *
  24516. * @name Highcharts.Point#name
  24517. * @type {string}
  24518. */
  24519. this.name = void 0;
  24520. /**
  24521. * The point's options as applied in the initial configuration, or
  24522. * extended through `Point.update`.
  24523. *
  24524. * In TypeScript you have to extend `PointOptionsObject` via an
  24525. * additional interface to allow custom data options:
  24526. *
  24527. * ```
  24528. * declare interface PointOptionsObject {
  24529. * customProperty: string;
  24530. * }
  24531. * ```
  24532. *
  24533. * @name Highcharts.Point#options
  24534. * @type {Highcharts.PointOptionsObject}
  24535. */
  24536. this.options = void 0;
  24537. /**
  24538. * The percentage for points in a stacked series, pies or gauges.
  24539. *
  24540. * @name Highcharts.Point#percentage
  24541. * @type {number|undefined}
  24542. */
  24543. this.percentage = void 0;
  24544. this.selected = false;
  24545. /**
  24546. * The series object associated with the point.
  24547. *
  24548. * @name Highcharts.Point#series
  24549. * @type {Highcharts.Series}
  24550. */
  24551. this.series = void 0;
  24552. /**
  24553. * The attributes of the rendered SVG shape like in `column` or `pie`
  24554. * series.
  24555. *
  24556. * @readonly
  24557. * @name Highcharts.Point#shapeArgs
  24558. * @type {Readonly<Highcharts.SVGAttributes>|undefined}
  24559. */
  24560. this.shapeArgs = void 0;
  24561. /**
  24562. * The total of values in either a stack for stacked series, or a pie in a
  24563. * pie series.
  24564. *
  24565. * @name Highcharts.Point#total
  24566. * @type {number|undefined}
  24567. */
  24568. this.total = void 0;
  24569. /**
  24570. * For certain series types, like pie charts, where individual points can
  24571. * be shown or hidden.
  24572. *
  24573. * @name Highcharts.Point#visible
  24574. * @type {boolean}
  24575. * @default true
  24576. */
  24577. this.visible = true;
  24578. this.x = void 0;
  24579. }
  24580. /* *
  24581. *
  24582. * Functions
  24583. *
  24584. * */
  24585. /**
  24586. * Animate SVG elements associated with the point.
  24587. *
  24588. * @private
  24589. * @function Highcharts.Point#animateBeforeDestroy
  24590. */
  24591. animateBeforeDestroy() {
  24592. const point = this, animateParams = { x: point.startXPos, opacity: 0 }, graphicalProps = point.getGraphicalProps();
  24593. graphicalProps.singular.forEach(function (prop) {
  24594. const isDataLabel = prop === 'dataLabel';
  24595. point[prop] = point[prop].animate(isDataLabel ? {
  24596. x: point[prop].startXPos,
  24597. y: point[prop].startYPos,
  24598. opacity: 0
  24599. } : animateParams);
  24600. });
  24601. graphicalProps.plural.forEach(function (plural) {
  24602. point[plural].forEach(function (item) {
  24603. if (item.element) {
  24604. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  24605. x: item.startXPos,
  24606. y: item.startYPos
  24607. } : {})));
  24608. }
  24609. });
  24610. });
  24611. }
  24612. /**
  24613. * Apply the options containing the x and y data and possible some extra
  24614. * properties. Called on point init or from point.update.
  24615. *
  24616. * @private
  24617. * @function Highcharts.Point#applyOptions
  24618. *
  24619. * @param {Highcharts.PointOptionsType} options
  24620. * The point options as defined in series.data.
  24621. *
  24622. * @param {number} [x]
  24623. * Optionally, the x value.
  24624. *
  24625. * @return {Highcharts.Point}
  24626. * The Point instance.
  24627. */
  24628. applyOptions(options, x) {
  24629. const point = this, series = point.series, pointValKey = series.options.pointValKey || series.pointValKey;
  24630. options = Point.prototype.optionsToObject.call(this, options);
  24631. // copy options directly to point
  24632. extend(point, options);
  24633. point.options = point.options ?
  24634. extend(point.options, options) :
  24635. options;
  24636. // Since options are copied into the Point instance, some accidental
  24637. // options must be shielded (#5681)
  24638. if (options.group) {
  24639. delete point.group;
  24640. }
  24641. if (options.dataLabels) {
  24642. delete point.dataLabels;
  24643. }
  24644. /**
  24645. * The y value of the point.
  24646. * @name Highcharts.Point#y
  24647. * @type {number|undefined}
  24648. */
  24649. // For higher dimension series types. For instance, for ranges, point.y
  24650. // is mapped to point.low.
  24651. if (pointValKey) {
  24652. point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
  24653. }
  24654. point.isNull = this.isValid && !this.isValid();
  24655. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  24656. // The point is initially selected by options (#5777)
  24657. if (point.selected) {
  24658. point.state = 'select';
  24659. }
  24660. /**
  24661. * The x value of the point.
  24662. * @name Highcharts.Point#x
  24663. * @type {number}
  24664. */
  24665. // If no x is set by now, get auto incremented value. All points must
  24666. // have an x value, however the y value can be null to create a gap in
  24667. // the series
  24668. if ('name' in point &&
  24669. typeof x === 'undefined' &&
  24670. series.xAxis &&
  24671. series.xAxis.hasNames) {
  24672. point.x = series.xAxis.nameToX(point);
  24673. }
  24674. if (typeof point.x === 'undefined' && series) {
  24675. if (typeof x === 'undefined') {
  24676. point.x = series.autoIncrement();
  24677. }
  24678. else {
  24679. point.x = x;
  24680. }
  24681. }
  24682. else if (isNumber(options.x) && series.options.relativeXValue) {
  24683. point.x = series.autoIncrement(options.x);
  24684. }
  24685. return point;
  24686. }
  24687. /**
  24688. * Destroy a point to clear memory. Its reference still stays in
  24689. * `series.data`.
  24690. *
  24691. * @private
  24692. * @function Highcharts.Point#destroy
  24693. */
  24694. destroy() {
  24695. if (!this.destroyed) {
  24696. const point = this, series = point.series, chart = series.chart, dataSorting = series.options.dataSorting, hoverPoints = chart.hoverPoints, globalAnimation = point.series.chart.renderer.globalAnimation, animation = animObject(globalAnimation);
  24697. /**
  24698. * Allow to call after animation.
  24699. * @private
  24700. */
  24701. const destroyPoint = () => {
  24702. // Remove all events and elements
  24703. if (point.graphic ||
  24704. point.graphics ||
  24705. point.dataLabel ||
  24706. point.dataLabels) {
  24707. removeEvent(point);
  24708. point.destroyElements();
  24709. }
  24710. for (const prop in point) { // eslint-disable-line guard-for-in
  24711. delete point[prop];
  24712. }
  24713. };
  24714. if (point.legendItem) {
  24715. // pies have legend items
  24716. chart.legend.destroyItem(point);
  24717. }
  24718. if (hoverPoints) {
  24719. point.setState();
  24720. erase(hoverPoints, point);
  24721. if (!hoverPoints.length) {
  24722. chart.hoverPoints = null;
  24723. }
  24724. }
  24725. if (point === chart.hoverPoint) {
  24726. point.onMouseOut();
  24727. }
  24728. // Remove properties after animation
  24729. if (!dataSorting || !dataSorting.enabled) {
  24730. destroyPoint();
  24731. }
  24732. else {
  24733. this.animateBeforeDestroy();
  24734. syncTimeout(destroyPoint, animation.duration);
  24735. }
  24736. chart.pointCount--;
  24737. }
  24738. this.destroyed = true;
  24739. }
  24740. /**
  24741. * Destroy SVG elements associated with the point.
  24742. *
  24743. * @private
  24744. * @function Highcharts.Point#destroyElements
  24745. * @param {Highcharts.Dictionary<number>} [kinds]
  24746. */
  24747. destroyElements(kinds) {
  24748. const point = this, props = point.getGraphicalProps(kinds);
  24749. props.singular.forEach(function (prop) {
  24750. point[prop] = point[prop].destroy();
  24751. });
  24752. props.plural.forEach(function (plural) {
  24753. point[plural].forEach(function (item) {
  24754. if (item && item.element) {
  24755. item.destroy();
  24756. }
  24757. });
  24758. delete point[plural];
  24759. });
  24760. }
  24761. /**
  24762. * Fire an event on the Point object.
  24763. *
  24764. * @private
  24765. * @function Highcharts.Point#firePointEvent
  24766. *
  24767. * @param {string} eventType
  24768. * Type of the event.
  24769. *
  24770. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  24771. * Additional event arguments.
  24772. *
  24773. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  24774. * Default event handler.
  24775. *
  24776. * @emits Highcharts.Point#event:*
  24777. */
  24778. firePointEvent(eventType, eventArgs, defaultFunction) {
  24779. const point = this, series = this.series, seriesOptions = series.options;
  24780. // load event handlers on demand to save time on mouseover/out
  24781. if (seriesOptions.point.events[eventType] ||
  24782. (point.options &&
  24783. point.options.events &&
  24784. point.options.events[eventType])) {
  24785. point.importEvents();
  24786. }
  24787. // add default handler if in selection mode
  24788. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  24789. defaultFunction = function (event) {
  24790. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  24791. // for Opera.
  24792. if (point.select) { // #2911
  24793. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  24794. }
  24795. };
  24796. }
  24797. fireEvent(point, eventType, eventArgs, defaultFunction);
  24798. }
  24799. /**
  24800. * Get the CSS class names for individual points. Used internally where the
  24801. * returned value is set on every point.
  24802. *
  24803. * @function Highcharts.Point#getClassName
  24804. *
  24805. * @return {string}
  24806. * The class names.
  24807. */
  24808. getClassName() {
  24809. const point = this;
  24810. return 'highcharts-point' +
  24811. (point.selected ? ' highcharts-point-select' : '') +
  24812. (point.negative ? ' highcharts-negative' : '') +
  24813. (point.isNull ? ' highcharts-null-point' : '') +
  24814. (typeof point.colorIndex !== 'undefined' ?
  24815. ' highcharts-color-' + point.colorIndex : '') +
  24816. (point.options.className ? ' ' + point.options.className : '') +
  24817. (point.zone && point.zone.className ? ' ' +
  24818. point.zone.className.replace('highcharts-negative', '') : '');
  24819. }
  24820. /**
  24821. * Get props of all existing graphical point elements.
  24822. *
  24823. * @private
  24824. * @function Highcharts.Point#getGraphicalProps
  24825. */
  24826. getGraphicalProps(kinds) {
  24827. const point = this, props = [], graphicalProps = { singular: [], plural: [] };
  24828. let prop, i;
  24829. kinds = kinds || { graphic: 1, dataLabel: 1 };
  24830. if (kinds.graphic) {
  24831. props.push('graphic');
  24832. }
  24833. if (kinds.dataLabel) {
  24834. props.push('dataLabel', 'dataLabelPath', 'dataLabelUpper', 'connector');
  24835. }
  24836. i = props.length;
  24837. while (i--) {
  24838. prop = props[i];
  24839. if (point[prop]) {
  24840. graphicalProps.singular.push(prop);
  24841. }
  24842. }
  24843. [
  24844. 'graphic',
  24845. 'dataLabel',
  24846. 'connector'
  24847. ].forEach(function (prop) {
  24848. const plural = prop + 's';
  24849. if (kinds[prop] && point[plural]) {
  24850. graphicalProps.plural.push(plural);
  24851. }
  24852. });
  24853. return graphicalProps;
  24854. }
  24855. /**
  24856. * Return the configuration hash needed for the data label and tooltip
  24857. * formatters.
  24858. *
  24859. * @function Highcharts.Point#getLabelConfig
  24860. *
  24861. * @return {Highcharts.PointLabelObject}
  24862. * Abstract object used in formatters and formats.
  24863. */
  24864. getLabelConfig() {
  24865. return {
  24866. x: this.category,
  24867. y: this.y,
  24868. color: this.color,
  24869. colorIndex: this.colorIndex,
  24870. key: this.name || this.category,
  24871. series: this.series,
  24872. point: this,
  24873. percentage: this.percentage,
  24874. total: this.total || this.stackTotal
  24875. };
  24876. }
  24877. /**
  24878. * Returns the value of the point property for a given value.
  24879. * @private
  24880. */
  24881. getNestedProperty(key) {
  24882. if (!key) {
  24883. return;
  24884. }
  24885. if (key.indexOf('custom.') === 0) {
  24886. return getNestedProperty(key, this.options);
  24887. }
  24888. return this[key];
  24889. }
  24890. /**
  24891. * In a series with `zones`, return the zone that the point belongs to.
  24892. *
  24893. * @function Highcharts.Point#getZone
  24894. *
  24895. * @return {Highcharts.SeriesZonesOptionsObject}
  24896. * The zone item.
  24897. */
  24898. getZone() {
  24899. const series = this.series, zones = series.zones, zoneAxis = series.zoneAxis || 'y';
  24900. let zone, i = 0;
  24901. zone = zones[i];
  24902. while (this[zoneAxis] >= zone.value) {
  24903. zone = zones[++i];
  24904. }
  24905. // For resetting or reusing the point (#8100)
  24906. if (!this.nonZonedColor) {
  24907. this.nonZonedColor = this.color;
  24908. }
  24909. if (zone && zone.color && !this.options.color) {
  24910. this.color = zone.color;
  24911. }
  24912. else {
  24913. this.color = this.nonZonedColor;
  24914. }
  24915. return zone;
  24916. }
  24917. /**
  24918. * Utility to check if point has new shape type. Used in column series and
  24919. * all others that are based on column series.
  24920. * @private
  24921. */
  24922. hasNewShapeType() {
  24923. const point = this;
  24924. const oldShapeType = point.graphic &&
  24925. (point.graphic.symbolName || point.graphic.element.nodeName);
  24926. return oldShapeType !== this.shapeType;
  24927. }
  24928. /**
  24929. * Initialize the point. Called internally based on the `series.data`
  24930. * option.
  24931. *
  24932. * @function Highcharts.Point#init
  24933. *
  24934. * @param {Highcharts.Series} series
  24935. * The series object containing this point.
  24936. *
  24937. * @param {Highcharts.PointOptionsType} options
  24938. * The data in either number, array or object format.
  24939. *
  24940. * @param {number} [x]
  24941. * Optionally, the X value of the point.
  24942. *
  24943. * @return {Highcharts.Point}
  24944. * The Point instance.
  24945. *
  24946. * @emits Highcharts.Point#event:afterInit
  24947. */
  24948. init(series, options, x) {
  24949. this.series = series;
  24950. this.applyOptions(options, x);
  24951. // Add a unique ID to the point if none is assigned
  24952. this.id = defined(this.id) ? this.id : uniqueKey();
  24953. this.resolveColor();
  24954. series.chart.pointCount++;
  24955. fireEvent(this, 'afterInit');
  24956. return this;
  24957. }
  24958. /**
  24959. * Determine if point is valid.
  24960. * @private
  24961. * @function Highcharts.Point#isValid
  24962. */
  24963. isValid() {
  24964. return this.x !== null && isNumber(this.y);
  24965. }
  24966. /**
  24967. * Transform number or array configs into objects. Also called for object
  24968. * configs. Used internally to unify the different configuration formats for
  24969. * points. For example, a simple number `10` in a line series will be
  24970. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  24971. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  24972. *
  24973. * @deprecated
  24974. * @function Highcharts.Point#optionsToObject
  24975. *
  24976. * @param {Highcharts.PointOptionsType} options
  24977. * Series data options.
  24978. *
  24979. * @return {Highcharts.Dictionary<*>}
  24980. * Transformed point options.
  24981. */
  24982. optionsToObject(options) {
  24983. const series = this.series, keys = series.options.keys, pointArrayMap = keys || series.pointArrayMap || ['y'], valueCount = pointArrayMap.length;
  24984. let ret = {}, firstItemType, i = 0, j = 0;
  24985. if (isNumber(options) || options === null) {
  24986. ret[pointArrayMap[0]] = options;
  24987. }
  24988. else if (isArray(options)) {
  24989. // with leading x value
  24990. if (!keys && options.length > valueCount) {
  24991. firstItemType = typeof options[0];
  24992. if (firstItemType === 'string') {
  24993. ret.name = options[0];
  24994. }
  24995. else if (firstItemType === 'number') {
  24996. ret.x = options[0];
  24997. }
  24998. i++;
  24999. }
  25000. while (j < valueCount) {
  25001. // Skip undefined positions for keys
  25002. if (!keys || typeof options[i] !== 'undefined') {
  25003. if (pointArrayMap[j].indexOf('.') > 0) {
  25004. // Handle nested keys, e.g. ['color.pattern.image']
  25005. // Avoid function call unless necessary.
  25006. Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  25007. }
  25008. else {
  25009. ret[pointArrayMap[j]] = options[i];
  25010. }
  25011. }
  25012. i++;
  25013. j++;
  25014. }
  25015. }
  25016. else if (typeof options === 'object') {
  25017. ret = options;
  25018. // This is the fastest way to detect if there are individual point
  25019. // dataLabels that need to be considered in drawDataLabels. These
  25020. // can only occur in object configs.
  25021. if (options.dataLabels) {
  25022. series._hasPointLabels = true;
  25023. }
  25024. // Same approach as above for markers
  25025. if (options.marker) {
  25026. series._hasPointMarkers = true;
  25027. }
  25028. }
  25029. return ret;
  25030. }
  25031. /**
  25032. * Get the pixel position of the point relative to the plot area.
  25033. * @private
  25034. * @function Highcharts.Point#pos
  25035. */
  25036. pos(chartCoordinates, plotY = this.plotY) {
  25037. if (!this.destroyed) {
  25038. const { plotX, series } = this, { chart, xAxis, yAxis } = series;
  25039. let posX = 0, posY = 0;
  25040. if (isNumber(plotX) && isNumber(plotY)) {
  25041. if (chartCoordinates) {
  25042. posX = xAxis ? xAxis.pos : chart.plotLeft;
  25043. posY = yAxis ? yAxis.pos : chart.plotTop;
  25044. }
  25045. return chart.inverted && xAxis && yAxis ?
  25046. [yAxis.len - plotY + posY, xAxis.len - plotX + posX] :
  25047. [plotX + posX, plotY + posY];
  25048. }
  25049. }
  25050. }
  25051. /**
  25052. * @private
  25053. * @function Highcharts.Point#resolveColor
  25054. */
  25055. resolveColor() {
  25056. const series = this.series, optionsChart = series.chart.options.chart, styledMode = series.chart.styledMode;
  25057. let color, colors, colorCount = optionsChart.colorCount, colorIndex;
  25058. // remove points nonZonedColor for later recalculation
  25059. delete this.nonZonedColor;
  25060. if (series.options.colorByPoint) {
  25061. if (!styledMode) {
  25062. colors = series.options.colors || series.chart.options.colors;
  25063. color = colors[series.colorCounter];
  25064. colorCount = colors.length;
  25065. }
  25066. colorIndex = series.colorCounter;
  25067. series.colorCounter++;
  25068. // loop back to zero
  25069. if (series.colorCounter === colorCount) {
  25070. series.colorCounter = 0;
  25071. }
  25072. }
  25073. else {
  25074. if (!styledMode) {
  25075. color = series.color;
  25076. }
  25077. colorIndex = series.colorIndex;
  25078. }
  25079. /**
  25080. * The point's current color index, used in styled mode instead of
  25081. * `color`. The color index is inserted in class names used for styling.
  25082. *
  25083. * @name Highcharts.Point#colorIndex
  25084. * @type {number|undefined}
  25085. */
  25086. this.colorIndex = pick(this.options.colorIndex, colorIndex);
  25087. /**
  25088. * The point's current color.
  25089. *
  25090. * @name Highcharts.Point#color
  25091. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  25092. */
  25093. this.color = pick(this.options.color, color);
  25094. }
  25095. /**
  25096. * Set a value in an object, on the property defined by key. The key
  25097. * supports nested properties using dot notation. The function modifies the
  25098. * input object and does not make a copy.
  25099. *
  25100. * @function Highcharts.Point#setNestedProperty<T>
  25101. *
  25102. * @param {T} object
  25103. * The object to set the value on.
  25104. *
  25105. * @param {*} value
  25106. * The value to set.
  25107. *
  25108. * @param {string} key
  25109. * Key to the property to set.
  25110. *
  25111. * @return {T}
  25112. * The modified object.
  25113. */
  25114. setNestedProperty(object, value, key) {
  25115. const nestedKeys = key.split('.');
  25116. nestedKeys.reduce(function (result, key, i, arr) {
  25117. const isLastKey = arr.length - 1 === i;
  25118. result[key] = (isLastKey ?
  25119. value :
  25120. isObject(result[key], true) ?
  25121. result[key] :
  25122. {});
  25123. return result[key];
  25124. }, object);
  25125. return object;
  25126. }
  25127. shouldDraw() {
  25128. return !this.isNull;
  25129. }
  25130. /**
  25131. * Extendable method for formatting each point's tooltip line.
  25132. *
  25133. * @function Highcharts.Point#tooltipFormatter
  25134. *
  25135. * @param {string} pointFormat
  25136. * The point format.
  25137. *
  25138. * @return {string}
  25139. * A string to be concatenated in to the common tooltip text.
  25140. */
  25141. tooltipFormatter(pointFormat) {
  25142. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  25143. const series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  25144. // Replace default point style with class name
  25145. if (series.chart.styledMode) {
  25146. pointFormat =
  25147. series.chart.tooltip.styledModeFormat(pointFormat);
  25148. }
  25149. // Loop over the point array map and replace unformatted values with
  25150. // sprintf formatting markup
  25151. (series.pointArrayMap || ['y']).forEach(function (key) {
  25152. key = '{point.' + key; // without the closing bracket
  25153. if (valuePrefix || valueSuffix) {
  25154. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  25155. }
  25156. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  25157. });
  25158. return format(pointFormat, {
  25159. point: this,
  25160. series: this.series
  25161. }, series.chart);
  25162. }
  25163. /**
  25164. * Update point with new options (typically x/y data) and optionally redraw
  25165. * the series.
  25166. *
  25167. * @sample highcharts/members/point-update-column/
  25168. * Update column value
  25169. * @sample highcharts/members/point-update-pie/
  25170. * Update pie slice
  25171. * @sample maps/members/point-update/
  25172. * Update map area value in Highmaps
  25173. *
  25174. * @function Highcharts.Point#update
  25175. *
  25176. * @param {Highcharts.PointOptionsType} options
  25177. * The point options. Point options are handled as described under
  25178. * the `series.type.data` item for each series type. For example
  25179. * for a line series, if options is a single number, the point will
  25180. * be given that number as the marin y value. If it is an array, it
  25181. * will be interpreted as x and y values respectively. If it is an
  25182. * object, advanced options are applied.
  25183. *
  25184. * @param {boolean} [redraw=true]
  25185. * Whether to redraw the chart after the point is updated. If doing
  25186. * more operations on the chart, it is best practice to set
  25187. * `redraw` to false and call `chart.redraw()` after.
  25188. *
  25189. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  25190. * Whether to apply animation, and optionally animation
  25191. * configuration.
  25192. *
  25193. * @emits Highcharts.Point#event:update
  25194. */
  25195. update(options, redraw, animation, runEvent) {
  25196. const point = this, series = point.series, graphic = point.graphic, chart = series.chart, seriesOptions = series.options;
  25197. let i;
  25198. redraw = pick(redraw, true);
  25199. /**
  25200. * @private
  25201. */
  25202. function update() {
  25203. point.applyOptions(options);
  25204. // Update visuals, #4146
  25205. // Handle mock graphic elements for a11y, #12718
  25206. const hasMockGraphic = graphic && point.hasMockGraphic;
  25207. const shouldDestroyGraphic = point.y === null ?
  25208. !hasMockGraphic :
  25209. hasMockGraphic;
  25210. if (graphic && shouldDestroyGraphic) {
  25211. point.graphic = graphic.destroy();
  25212. delete point.hasMockGraphic;
  25213. }
  25214. if (isObject(options, true)) {
  25215. // Destroy so we can get new elements
  25216. if (graphic && graphic.element) {
  25217. // "null" is also a valid symbol
  25218. if (options &&
  25219. options.marker &&
  25220. typeof options.marker.symbol !== 'undefined') {
  25221. point.graphic = graphic.destroy();
  25222. }
  25223. }
  25224. if (options && options.dataLabels && point.dataLabel) {
  25225. point.dataLabel = point.dataLabel.destroy(); // #2468
  25226. }
  25227. if (point.connector) {
  25228. point.connector = point.connector.destroy(); // #7243
  25229. }
  25230. }
  25231. // record changes in the parallel arrays
  25232. i = point.index;
  25233. series.updateParallelArrays(point, i);
  25234. // Record the options to options.data. If the old or the new config
  25235. // is an object, use point options, otherwise use raw options
  25236. // (#4701, #4916).
  25237. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  25238. isObject(options, true)) ?
  25239. point.options :
  25240. pick(options, seriesOptions.data[i]);
  25241. // redraw
  25242. series.isDirty = series.isDirtyData = true;
  25243. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  25244. chart.isDirtyBox = true;
  25245. }
  25246. if (seriesOptions.legendType === 'point') { // #1831, #1885
  25247. chart.isDirtyLegend = true;
  25248. }
  25249. if (redraw) {
  25250. chart.redraw(animation);
  25251. }
  25252. }
  25253. // Fire the event with a default handler of doing the update
  25254. if (runEvent === false) { // When called from setData
  25255. update();
  25256. }
  25257. else {
  25258. point.firePointEvent('update', { options: options }, update);
  25259. }
  25260. }
  25261. /**
  25262. * Remove a point and optionally redraw the series and if necessary the axes
  25263. *
  25264. * @sample highcharts/plotoptions/series-point-events-remove/
  25265. * Remove point and confirm
  25266. * @sample highcharts/members/point-remove/
  25267. * Remove pie slice
  25268. * @sample maps/members/point-remove/
  25269. * Remove selected points in Highmaps
  25270. *
  25271. * @function Highcharts.Point#remove
  25272. *
  25273. * @param {boolean} [redraw=true]
  25274. * Whether to redraw the chart or wait for an explicit call. When
  25275. * doing more operations on the chart, for example running
  25276. * `point.remove()` in a loop, it is best practice to set `redraw`
  25277. * to false and call `chart.redraw()` after.
  25278. *
  25279. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
  25280. * Whether to apply animation, and optionally animation
  25281. * configuration.
  25282. */
  25283. remove(redraw, animation) {
  25284. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  25285. }
  25286. /**
  25287. * Toggle the selection status of a point.
  25288. *
  25289. * @see Highcharts.Chart#getSelectedPoints
  25290. *
  25291. * @sample highcharts/members/point-select/
  25292. * Select a point from a button
  25293. * @sample highcharts/chart/events-selection-points/
  25294. * Select a range of points through a drag selection
  25295. * @sample maps/series/data-id/
  25296. * Select a point in Highmaps
  25297. *
  25298. * @function Highcharts.Point#select
  25299. *
  25300. * @param {boolean} [selected]
  25301. * When `true`, the point is selected. When `false`, the point is
  25302. * unselected. When `null` or `undefined`, the selection state is toggled.
  25303. *
  25304. * @param {boolean} [accumulate=false]
  25305. * When `true`, the selection is added to other selected points.
  25306. * When `false`, other selected points are deselected. Internally in
  25307. * Highcharts, when
  25308. * [allowPointSelect](https://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  25309. * is `true`, selected points are accumulated on Control, Shift or Cmd
  25310. * clicking the point.
  25311. *
  25312. * @emits Highcharts.Point#event:select
  25313. * @emits Highcharts.Point#event:unselect
  25314. */
  25315. select(selected, accumulate) {
  25316. const point = this, series = point.series, chart = series.chart;
  25317. selected = pick(selected, !point.selected);
  25318. this.selectedStaging = selected;
  25319. // fire the event with the default handler
  25320. point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
  25321. /**
  25322. * Whether the point is selected or not.
  25323. *
  25324. * @see Point#select
  25325. * @see Chart#getSelectedPoints
  25326. *
  25327. * @name Highcharts.Point#selected
  25328. * @type {boolean}
  25329. */
  25330. point.selected = point.options.selected = selected;
  25331. series.options.data[series.data.indexOf(point)] =
  25332. point.options;
  25333. point.setState(selected && 'select');
  25334. // unselect all other points unless Ctrl or Cmd + click
  25335. if (!accumulate) {
  25336. chart.getSelectedPoints().forEach(function (loopPoint) {
  25337. const loopSeries = loopPoint.series;
  25338. if (loopPoint.selected && loopPoint !== point) {
  25339. loopPoint.selected = loopPoint.options.selected =
  25340. false;
  25341. loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
  25342. // Programatically selecting a point should restore
  25343. // normal state, but when click happened on other
  25344. // point, set inactive state to match other points
  25345. loopPoint.setState(chart.hoverPoints &&
  25346. loopSeries.options.inactiveOtherPoints ?
  25347. 'inactive' : '');
  25348. loopPoint.firePointEvent('unselect');
  25349. }
  25350. });
  25351. }
  25352. });
  25353. delete this.selectedStaging;
  25354. }
  25355. /**
  25356. * Runs on mouse over the point. Called internally from mouse and touch
  25357. * events.
  25358. *
  25359. * @function Highcharts.Point#onMouseOver
  25360. *
  25361. * @param {Highcharts.PointerEventObject} [e]
  25362. * The event arguments.
  25363. */
  25364. onMouseOver(e) {
  25365. const point = this, series = point.series, chart = series.chart, pointer = chart.pointer;
  25366. e = e ?
  25367. pointer.normalize(e) :
  25368. // In cases where onMouseOver is called directly without an event
  25369. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  25370. pointer.runPointActions(e, point);
  25371. }
  25372. /**
  25373. * Runs on mouse out from the point. Called internally from mouse and touch
  25374. * events.
  25375. *
  25376. * @function Highcharts.Point#onMouseOut
  25377. * @emits Highcharts.Point#event:mouseOut
  25378. */
  25379. onMouseOut() {
  25380. const point = this, chart = point.series.chart;
  25381. point.firePointEvent('mouseOut');
  25382. if (!point.series.options.inactiveOtherPoints) {
  25383. (chart.hoverPoints || []).forEach(function (p) {
  25384. p.setState();
  25385. });
  25386. }
  25387. chart.hoverPoints = chart.hoverPoint = null;
  25388. }
  25389. /**
  25390. * Import events from the series' and point's options. Only do it on
  25391. * demand, to save processing time on hovering.
  25392. *
  25393. * @private
  25394. * @function Highcharts.Point#importEvents
  25395. */
  25396. importEvents() {
  25397. if (!this.hasImportedEvents) {
  25398. const point = this, options = merge(point.series.options.point, point.options), events = options.events;
  25399. point.events = events;
  25400. objectEach(events, function (event, eventType) {
  25401. if (isFunction(event)) {
  25402. addEvent(point, eventType, event);
  25403. }
  25404. });
  25405. this.hasImportedEvents = true;
  25406. }
  25407. }
  25408. /**
  25409. * Set the point's state.
  25410. *
  25411. * @function Highcharts.Point#setState
  25412. *
  25413. * @param {Highcharts.PointStateValue|""} [state]
  25414. * The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
  25415. * or `''` (an empty string), `'normal'` or `undefined` to set to
  25416. * normal state.
  25417. * @param {boolean} [move]
  25418. * State for animation.
  25419. *
  25420. * @emits Highcharts.Point#event:afterSetState
  25421. */
  25422. setState(state, move) {
  25423. const point = this, series = point.series, previousState = point.state, stateOptions = (series.options.states[state || 'normal'] ||
  25424. {}), markerOptions = (defaultOptions.plotOptions[series.type].marker &&
  25425. series.options.marker), normalDisabled = (markerOptions && markerOptions.enabled === false), markerStateOptions = ((markerOptions &&
  25426. markerOptions.states &&
  25427. markerOptions.states[state || 'normal']) || {}), stateDisabled = markerStateOptions.enabled === false, pointMarker = point.marker || {}, chart = series.chart, hasMarkers = (markerOptions && series.markerAttribs);
  25428. let halo = series.halo, markerAttribs, pointAttribs, pointAttribsAnimation, stateMarkerGraphic = series.stateMarkerGraphic, newSymbol;
  25429. state = state || ''; // empty string
  25430. if (
  25431. // already has this state
  25432. (state === point.state && !move) ||
  25433. // selected points don't respond to hover
  25434. (point.selected && state !== 'select') ||
  25435. // series' state options is disabled
  25436. (stateOptions.enabled === false) ||
  25437. // general point marker's state options is disabled
  25438. (state && (stateDisabled ||
  25439. (normalDisabled &&
  25440. markerStateOptions.enabled === false))) ||
  25441. // individual point marker's state options is disabled
  25442. (state &&
  25443. pointMarker.states &&
  25444. pointMarker.states[state] &&
  25445. pointMarker.states[state].enabled === false) // #1610
  25446. ) {
  25447. return;
  25448. }
  25449. point.state = state;
  25450. if (hasMarkers) {
  25451. markerAttribs = series.markerAttribs(point, state);
  25452. }
  25453. // Apply hover styles to the existing point
  25454. // Prevent from mocked null points (#14966)
  25455. if (point.graphic && !point.hasMockGraphic) {
  25456. if (previousState) {
  25457. point.graphic.removeClass('highcharts-point-' + previousState);
  25458. }
  25459. if (state) {
  25460. point.graphic.addClass('highcharts-point-' + state);
  25461. }
  25462. if (!chart.styledMode) {
  25463. pointAttribs = series.pointAttribs(point, state);
  25464. pointAttribsAnimation = pick(chart.options.chart.animation, stateOptions.animation);
  25465. const opacity = pointAttribs.opacity;
  25466. // Some inactive points (e.g. slices in pie) should apply
  25467. // opacity also for their labels
  25468. if (series.options.inactiveOtherPoints && isNumber(opacity)) {
  25469. (point.dataLabels || []).forEach(function (label) {
  25470. if (label &&
  25471. !label.hasClass('highcharts-data-label-hidden')) {
  25472. label.animate({ opacity }, pointAttribsAnimation);
  25473. }
  25474. });
  25475. if (point.connector) {
  25476. point.connector.animate({ opacity }, pointAttribsAnimation);
  25477. }
  25478. }
  25479. point.graphic.animate(pointAttribs, pointAttribsAnimation);
  25480. }
  25481. if (markerAttribs) {
  25482. point.graphic.animate(markerAttribs, pick(
  25483. // Turn off globally:
  25484. chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
  25485. }
  25486. // Zooming in from a range with no markers to a range with markers
  25487. if (stateMarkerGraphic) {
  25488. stateMarkerGraphic.hide();
  25489. }
  25490. }
  25491. else {
  25492. // if a graphic is not applied to each point in the normal state,
  25493. // create a shared graphic for the hover state
  25494. if (state && markerStateOptions) {
  25495. newSymbol = pointMarker.symbol || series.symbol;
  25496. // If the point has another symbol than the previous one, throw
  25497. // away the state marker graphic and force a new one (#1459)
  25498. if (stateMarkerGraphic &&
  25499. stateMarkerGraphic.currentSymbol !== newSymbol) {
  25500. stateMarkerGraphic = stateMarkerGraphic.destroy();
  25501. }
  25502. // Add a new state marker graphic
  25503. if (markerAttribs) {
  25504. if (!stateMarkerGraphic) {
  25505. if (newSymbol) {
  25506. series.stateMarkerGraphic = stateMarkerGraphic =
  25507. chart.renderer
  25508. .symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height)
  25509. .add(series.markerGroup);
  25510. stateMarkerGraphic.currentSymbol = newSymbol;
  25511. }
  25512. // Move the existing graphic
  25513. }
  25514. else {
  25515. stateMarkerGraphic[move ? 'animate' : 'attr']({
  25516. x: markerAttribs.x,
  25517. y: markerAttribs.y
  25518. });
  25519. }
  25520. }
  25521. if (!chart.styledMode && stateMarkerGraphic &&
  25522. point.state !== 'inactive') {
  25523. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  25524. }
  25525. }
  25526. if (stateMarkerGraphic) {
  25527. stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
  25528. stateMarkerGraphic.element.point = point; // #4310
  25529. stateMarkerGraphic.addClass(point.getClassName(), true);
  25530. }
  25531. }
  25532. // Show me your halo
  25533. const haloOptions = stateOptions.halo;
  25534. const markerGraphic = (point.graphic || stateMarkerGraphic);
  25535. const markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
  25536. if (haloOptions &&
  25537. haloOptions.size &&
  25538. markerGraphic &&
  25539. markerVisibility !== 'hidden' &&
  25540. !point.isCluster) {
  25541. if (!halo) {
  25542. series.halo = halo = chart.renderer.path()
  25543. // #5818, #5903, #6705
  25544. .add(markerGraphic.parentGroup);
  25545. }
  25546. halo.show()[move ? 'animate' : 'attr']({
  25547. d: point.haloPath(haloOptions.size)
  25548. });
  25549. halo.attr({
  25550. 'class': 'highcharts-halo highcharts-color-' +
  25551. pick(point.colorIndex, series.colorIndex) +
  25552. (point.className ? ' ' + point.className : ''),
  25553. 'visibility': markerVisibility,
  25554. 'zIndex': -1 // #4929, #8276
  25555. });
  25556. halo.point = point; // #6055
  25557. if (!chart.styledMode) {
  25558. halo.attr(extend({
  25559. 'fill': point.color || series.color,
  25560. 'fill-opacity': haloOptions.opacity
  25561. }, AST.filterUserAttributes(haloOptions.attributes || {})));
  25562. }
  25563. }
  25564. else if (halo && halo.point && halo.point.haloPath) {
  25565. // Animate back to 0 on the current halo point (#6055)
  25566. halo.animate({ d: halo.point.haloPath(0) }, null,
  25567. // Hide after unhovering. The `complete` callback runs in the
  25568. // halo's context (#7681).
  25569. halo.hide);
  25570. }
  25571. fireEvent(point, 'afterSetState', { state });
  25572. }
  25573. /**
  25574. * Get the path definition for the halo, which is usually a shadow-like
  25575. * circle around the currently hovered point.
  25576. *
  25577. * @function Highcharts.Point#haloPath
  25578. *
  25579. * @param {number} size
  25580. * The radius of the circular halo.
  25581. *
  25582. * @return {Highcharts.SVGPathArray}
  25583. * The path definition.
  25584. */
  25585. haloPath(size) {
  25586. const pos = this.pos();
  25587. return pos ? this.series.chart.renderer.symbols.circle(Math.floor(pos[0]) - size, pos[1] - size, size * 2, size * 2) : [];
  25588. }
  25589. }
  25590. /* *
  25591. *
  25592. * Default Export
  25593. *
  25594. * */
  25595. /* *
  25596. *
  25597. * API Declarations
  25598. *
  25599. * */
  25600. /**
  25601. * Function callback when a series point is clicked. Return false to cancel the
  25602. * action.
  25603. *
  25604. * @callback Highcharts.PointClickCallbackFunction
  25605. *
  25606. * @param {Highcharts.Point} this
  25607. * The point where the event occured.
  25608. *
  25609. * @param {Highcharts.PointClickEventObject} event
  25610. * Event arguments.
  25611. */
  25612. /**
  25613. * Common information for a click event on a series point.
  25614. *
  25615. * @interface Highcharts.PointClickEventObject
  25616. * @extends Highcharts.PointerEventObject
  25617. */ /**
  25618. * Clicked point.
  25619. * @name Highcharts.PointClickEventObject#point
  25620. * @type {Highcharts.Point}
  25621. */
  25622. /**
  25623. * Configuration for the data label and tooltip formatters.
  25624. *
  25625. * @interface Highcharts.PointLabelObject
  25626. */ /**
  25627. * The point's current color.
  25628. * @name Highcharts.PointLabelObject#color
  25629. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  25630. */ /**
  25631. * The point's current color index, used in styled mode instead of `color`. The
  25632. * color index is inserted in class names used for styling.
  25633. * @name Highcharts.PointLabelObject#colorIndex
  25634. * @type {number}
  25635. */ /**
  25636. * The name of the related point.
  25637. * @name Highcharts.PointLabelObject#key
  25638. * @type {string|undefined}
  25639. */ /**
  25640. * The percentage for related points in a stacked series or pies.
  25641. * @name Highcharts.PointLabelObject#percentage
  25642. * @type {number}
  25643. */ /**
  25644. * The related point. The point name, if defined, is available through
  25645. * `this.point.name`.
  25646. * @name Highcharts.PointLabelObject#point
  25647. * @type {Highcharts.Point}
  25648. */ /**
  25649. * The related series. The series name is available through `this.series.name`.
  25650. * @name Highcharts.PointLabelObject#series
  25651. * @type {Highcharts.Series}
  25652. */ /**
  25653. * The total of values in either a stack for stacked series, or a pie in a pie
  25654. * series.
  25655. * @name Highcharts.PointLabelObject#total
  25656. * @type {number|undefined}
  25657. */ /**
  25658. * For categorized axes this property holds the category name for the point. For
  25659. * other axes it holds the X value.
  25660. * @name Highcharts.PointLabelObject#x
  25661. * @type {number|string|undefined}
  25662. */ /**
  25663. * The y value of the point.
  25664. * @name Highcharts.PointLabelObject#y
  25665. * @type {number|null|undefined}
  25666. */
  25667. /**
  25668. * Gets fired when the mouse leaves the area close to the point.
  25669. *
  25670. * @callback Highcharts.PointMouseOutCallbackFunction
  25671. *
  25672. * @param {Highcharts.Point} this
  25673. * Point where the event occured.
  25674. *
  25675. * @param {global.PointerEvent} event
  25676. * Event that occured.
  25677. */
  25678. /**
  25679. * Gets fired when the mouse enters the area close to the point.
  25680. *
  25681. * @callback Highcharts.PointMouseOverCallbackFunction
  25682. *
  25683. * @param {Highcharts.Point} this
  25684. * Point where the event occured.
  25685. *
  25686. * @param {global.Event} event
  25687. * Event that occured.
  25688. */
  25689. /**
  25690. * The generic point options for all series.
  25691. *
  25692. * In TypeScript you have to extend `PointOptionsObject` with an additional
  25693. * declaration to allow custom data options:
  25694. *
  25695. * ```
  25696. * declare interface PointOptionsObject {
  25697. * customProperty: string;
  25698. * }
  25699. * ```
  25700. *
  25701. * @interface Highcharts.PointOptionsObject
  25702. */
  25703. /**
  25704. * Possible option types for a data point. Use `null` to indicate a gap.
  25705. *
  25706. * @typedef {number|string|Highcharts.PointOptionsObject|Array<(number|string|null)>|null} Highcharts.PointOptionsType
  25707. */
  25708. /**
  25709. * Gets fired when the point is removed using the `.remove()` method.
  25710. *
  25711. * @callback Highcharts.PointRemoveCallbackFunction
  25712. *
  25713. * @param {Highcharts.Point} this
  25714. * Point where the event occured.
  25715. *
  25716. * @param {global.Event} event
  25717. * Event that occured.
  25718. */
  25719. /**
  25720. * Possible key values for the point state options.
  25721. *
  25722. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  25723. */
  25724. /**
  25725. * Gets fired when the point is updated programmatically through the `.update()`
  25726. * method.
  25727. *
  25728. * @callback Highcharts.PointUpdateCallbackFunction
  25729. *
  25730. * @param {Highcharts.Point} this
  25731. * Point where the event occured.
  25732. *
  25733. * @param {Highcharts.PointUpdateEventObject} event
  25734. * Event that occured.
  25735. */
  25736. /**
  25737. * Information about the update event.
  25738. *
  25739. * @interface Highcharts.PointUpdateEventObject
  25740. * @extends global.Event
  25741. */ /**
  25742. * Options data of the update event.
  25743. * @name Highcharts.PointUpdateEventObject#options
  25744. * @type {Highcharts.PointOptionsType}
  25745. */
  25746. /**
  25747. * @interface Highcharts.PointEventsOptionsObject
  25748. */ /**
  25749. * Fires when the point is selected either programmatically or following a click
  25750. * on the point. One parameter, `event`, is passed to the function. Returning
  25751. * `false` cancels the operation.
  25752. * @name Highcharts.PointEventsOptionsObject#select
  25753. * @type {Highcharts.PointSelectCallbackFunction|undefined}
  25754. */ /**
  25755. * Fires when the point is unselected either programmatically or following a
  25756. * click on the point. One parameter, `event`, is passed to the function.
  25757. * Returning `false` cancels the operation.
  25758. * @name Highcharts.PointEventsOptionsObject#unselect
  25759. * @type {Highcharts.PointUnselectCallbackFunction|undefined}
  25760. */
  25761. /**
  25762. * Information about the select/unselect event.
  25763. *
  25764. * @interface Highcharts.PointInteractionEventObject
  25765. * @extends global.Event
  25766. */ /**
  25767. * @name Highcharts.PointInteractionEventObject#accumulate
  25768. * @type {boolean}
  25769. */
  25770. /**
  25771. * Gets fired when the point is selected either programmatically or following a
  25772. * click on the point.
  25773. *
  25774. * @callback Highcharts.PointSelectCallbackFunction
  25775. *
  25776. * @param {Highcharts.Point} this
  25777. * Point where the event occured.
  25778. *
  25779. * @param {Highcharts.PointInteractionEventObject} event
  25780. * Event that occured.
  25781. */
  25782. /**
  25783. * Fires when the point is unselected either programmatically or following a
  25784. * click on the point.
  25785. *
  25786. * @callback Highcharts.PointUnselectCallbackFunction
  25787. *
  25788. * @param {Highcharts.Point} this
  25789. * Point where the event occured.
  25790. *
  25791. * @param {Highcharts.PointInteractionEventObject} event
  25792. * Event that occured.
  25793. */
  25794. ''; // keeps doclets above in JS file.
  25795. return Point;
  25796. });
  25797. _registerModule(_modules, 'Core/Pointer.js', [_modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, H, U) {
  25798. /* *
  25799. *
  25800. * (c) 2010-2021 Torstein Honsi
  25801. *
  25802. * License: www.highcharts.com/license
  25803. *
  25804. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  25805. *
  25806. * */
  25807. const { parse: color } = Color;
  25808. const { charts, noop } = H;
  25809. const { addEvent, attr, css, defined, extend, find, fireEvent, isNumber, isObject, objectEach, offset, pick, splat } = U;
  25810. /* *
  25811. *
  25812. * Class
  25813. *
  25814. * */
  25815. /**
  25816. * The mouse and touch tracker object. Each {@link Chart} item has one
  25817. * associated Pointer item that can be accessed from the {@link Chart.pointer}
  25818. * property.
  25819. *
  25820. * @class
  25821. * @name Highcharts.Pointer
  25822. *
  25823. * @param {Highcharts.Chart} chart
  25824. * The chart instance.
  25825. *
  25826. * @param {Highcharts.Options} options
  25827. * The root options object. The pointer uses options from the chart and tooltip
  25828. * structures.
  25829. */
  25830. class Pointer {
  25831. /* *
  25832. *
  25833. * Constructors
  25834. *
  25835. * */
  25836. constructor(chart, options) {
  25837. this.lastValidTouch = {};
  25838. this.pinchDown = [];
  25839. this.runChartClick = false;
  25840. this.eventsToUnbind = [];
  25841. this.chart = chart;
  25842. this.hasDragged = false;
  25843. this.options = options;
  25844. this.init(chart, options);
  25845. }
  25846. /* *
  25847. *
  25848. * Functions
  25849. *
  25850. * */
  25851. /**
  25852. * Set inactive state to all series that are not currently hovered,
  25853. * or, if `inactiveOtherPoints` is set to true, set inactive state to
  25854. * all points within that series.
  25855. *
  25856. * @private
  25857. * @function Highcharts.Pointer#applyInactiveState
  25858. *
  25859. * @param {Array<Highcharts.Point>} points
  25860. * Currently hovered points
  25861. */
  25862. applyInactiveState(points) {
  25863. let activeSeries = [], series;
  25864. // Get all active series from the hovered points
  25865. (points || []).forEach(function (item) {
  25866. series = item.series;
  25867. // Include itself
  25868. activeSeries.push(series);
  25869. // Include parent series
  25870. if (series.linkedParent) {
  25871. activeSeries.push(series.linkedParent);
  25872. }
  25873. // Include all child series
  25874. if (series.linkedSeries) {
  25875. activeSeries = activeSeries.concat(series.linkedSeries);
  25876. }
  25877. // Include navigator series
  25878. if (series.navigatorSeries) {
  25879. activeSeries.push(series.navigatorSeries);
  25880. }
  25881. });
  25882. // Now loop over all series, filtering out active series
  25883. this.chart.series.forEach(function (inactiveSeries) {
  25884. if (activeSeries.indexOf(inactiveSeries) === -1) {
  25885. // Inactive series
  25886. inactiveSeries.setState('inactive', true);
  25887. }
  25888. else if (inactiveSeries.options.inactiveOtherPoints) {
  25889. // Active series, but other points should be inactivated
  25890. inactiveSeries.setAllPointsToState('inactive');
  25891. }
  25892. });
  25893. }
  25894. /**
  25895. * Destroys the Pointer object and disconnects DOM events.
  25896. *
  25897. * @function Highcharts.Pointer#destroy
  25898. */
  25899. destroy() {
  25900. const pointer = this;
  25901. this.eventsToUnbind.forEach((unbind) => unbind());
  25902. this.eventsToUnbind = [];
  25903. if (!H.chartCount) {
  25904. if (Pointer.unbindDocumentMouseUp) {
  25905. Pointer.unbindDocumentMouseUp = Pointer.unbindDocumentMouseUp();
  25906. }
  25907. if (Pointer.unbindDocumentTouchEnd) {
  25908. Pointer.unbindDocumentTouchEnd = (Pointer.unbindDocumentTouchEnd());
  25909. }
  25910. }
  25911. // memory and CPU leak
  25912. clearInterval(pointer.tooltipTimeout);
  25913. objectEach(pointer, function (_val, prop) {
  25914. pointer[prop] = void 0;
  25915. });
  25916. }
  25917. /**
  25918. * Calculate attrs for selection marker.
  25919. * @private
  25920. * @function Highcharts.Pointer#getSelectionMarkerAttrs
  25921. * @emits getSelectionMarkerAttrs
  25922. */
  25923. getSelectionMarkerAttrs(chartX, chartY) {
  25924. const e = {
  25925. args: { chartX, chartY },
  25926. attrs: {},
  25927. shapeType: 'rect'
  25928. };
  25929. fireEvent(this, 'getSelectionMarkerAttrs', e, (e) => {
  25930. const { chart, mouseDownX = 0, mouseDownY = 0, zoomHor, zoomVert } = this, attrs = e.attrs;
  25931. let size;
  25932. attrs.x = chart.plotLeft;
  25933. attrs.y = chart.plotTop;
  25934. attrs.width = zoomHor ? 1 : chart.plotWidth;
  25935. attrs.height = zoomVert ? 1 : chart.plotHeight;
  25936. // Adjust the width of the selection marker
  25937. if (zoomHor) {
  25938. size = chartX - mouseDownX;
  25939. attrs.width = Math.abs(size);
  25940. attrs.x = (size > 0 ? 0 : size) + mouseDownX;
  25941. }
  25942. // Adjust the height of the selection marker
  25943. if (zoomVert) {
  25944. size = chartY - mouseDownY;
  25945. attrs.height = Math.abs(size);
  25946. attrs.y = (size > 0 ? 0 : size) + mouseDownY;
  25947. }
  25948. });
  25949. return e;
  25950. }
  25951. /**
  25952. * Perform a drag operation in response to a mousemove event while the mouse
  25953. * is down.
  25954. * @private
  25955. * @function Highcharts.Pointer#drag
  25956. */
  25957. drag(e) {
  25958. const chart = this.chart, chartOptions = chart.options.chart, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, mouseDownX = (this.mouseDownX || 0), mouseDownY = (this.mouseDownY || 0), panningEnabled = isObject(chartOptions.panning) ?
  25959. chartOptions.panning && chartOptions.panning.enabled :
  25960. chartOptions.panning, panKey = (chartOptions.panKey && e[chartOptions.panKey + 'Key']);
  25961. let chartX = e.chartX, chartY = e.chartY, clickedInside, selectionMarker = this.selectionMarker;
  25962. // If the device supports both touch and mouse (like IE11), and we are
  25963. // touch-dragging inside the plot area, don't handle the mouse event.
  25964. // #4339.
  25965. if (selectionMarker && selectionMarker.touch) {
  25966. return;
  25967. }
  25968. // If the mouse is outside the plot area, adjust to coordinates
  25969. // inside to prevent the selection marker from going outside
  25970. if (chartX < plotLeft) {
  25971. chartX = plotLeft;
  25972. }
  25973. else if (chartX > plotLeft + plotWidth) {
  25974. chartX = plotLeft + plotWidth;
  25975. }
  25976. if (chartY < plotTop) {
  25977. chartY = plotTop;
  25978. }
  25979. else if (chartY > plotTop + plotHeight) {
  25980. chartY = plotTop + plotHeight;
  25981. }
  25982. // determine if the mouse has moved more than 10px
  25983. this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
  25984. Math.pow(mouseDownY - chartY, 2));
  25985. if (this.hasDragged > 10) {
  25986. clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop, {
  25987. visiblePlotOnly: true
  25988. });
  25989. const { shapeType, attrs } = this.getSelectionMarkerAttrs(chartX, chartY);
  25990. // make a selection
  25991. if ((chart.hasCartesianSeries || chart.mapView) &&
  25992. (this.zoomX || this.zoomY) &&
  25993. clickedInside &&
  25994. !panKey) {
  25995. if (!selectionMarker) {
  25996. this.selectionMarker = selectionMarker =
  25997. chart.renderer[shapeType]();
  25998. selectionMarker
  25999. .attr({
  26000. 'class': 'highcharts-selection-marker',
  26001. zIndex: 7
  26002. })
  26003. .add();
  26004. if (!chart.styledMode) {
  26005. selectionMarker.attr({
  26006. fill: chartOptions.selectionMarkerFill ||
  26007. color("#334eff" /* Palette.highlightColor80 */)
  26008. .setOpacity(0.25).get()
  26009. });
  26010. }
  26011. }
  26012. }
  26013. if (selectionMarker) {
  26014. selectionMarker.attr(attrs);
  26015. }
  26016. // panning
  26017. if (clickedInside &&
  26018. !selectionMarker &&
  26019. panningEnabled) {
  26020. chart.pan(e, chartOptions.panning);
  26021. }
  26022. }
  26023. }
  26024. /**
  26025. * Start a drag operation.
  26026. * @private
  26027. * @function Highcharts.Pointer#dragStart
  26028. */
  26029. dragStart(e) {
  26030. const chart = this.chart;
  26031. // Record the start position
  26032. chart.mouseIsDown = e.type;
  26033. chart.cancelClick = false;
  26034. chart.mouseDownX = this.mouseDownX = e.chartX;
  26035. chart.mouseDownY = this.mouseDownY = e.chartY;
  26036. }
  26037. /**
  26038. * Get selection box to calculate extremes
  26039. * @private
  26040. * @function Highcharts.Pointer#getSelectionBox
  26041. * @emits getSelectionBox
  26042. */
  26043. getSelectionBox(marker) {
  26044. const e = {
  26045. args: { marker },
  26046. result: {}
  26047. };
  26048. fireEvent(this, 'getSelectionBox', e, (e) => {
  26049. e.result = {
  26050. x: marker.attr ? +marker.attr('x') : marker.x,
  26051. y: marker.attr ? +marker.attr('y') : marker.y,
  26052. width: marker.attr ? marker.attr('width') : marker.width,
  26053. height: marker.attr ? marker.attr('height') : marker.height
  26054. };
  26055. });
  26056. return e.result;
  26057. }
  26058. /**
  26059. * On mouse up or touch end across the entire document, drop the selection.
  26060. * @private
  26061. * @function Highcharts.Pointer#drop
  26062. */
  26063. drop(e) {
  26064. const pointer = this, chart = this.chart, hasPinched = this.hasPinched;
  26065. if (this.selectionMarker) {
  26066. const { x, y, width, height } = this.getSelectionBox(this.selectionMarker);
  26067. const selectionData = {
  26068. originalEvent: e,
  26069. xAxis: [],
  26070. yAxis: [],
  26071. x,
  26072. y,
  26073. width,
  26074. height
  26075. };
  26076. // Start by false runZoom, unless when we have a mapView, in
  26077. // which case the zoom will be handled in the selection event.
  26078. let runZoom = Boolean(chart.mapView);
  26079. // a selection has been made
  26080. if (this.hasDragged || hasPinched) {
  26081. // record each axis' min and max
  26082. chart.axes.forEach(function (axis) {
  26083. if (axis.zoomEnabled &&
  26084. defined(axis.min) &&
  26085. (hasPinched ||
  26086. pointer[{
  26087. xAxis: 'zoomX',
  26088. yAxis: 'zoomY'
  26089. }[axis.coll]]) &&
  26090. isNumber(x) &&
  26091. isNumber(y) &&
  26092. isNumber(width) &&
  26093. isNumber(height)) { // #859, #3569
  26094. const horiz = axis.horiz, minPixelPadding = e.type === 'touchend' ?
  26095. axis.minPixelPadding :
  26096. 0, // #1207, #3075
  26097. selectionMin = axis.toValue((horiz ? x : y) + minPixelPadding), selectionMax = axis.toValue((horiz ? x + width : y + height) -
  26098. minPixelPadding);
  26099. selectionData[axis.coll].push({
  26100. axis: axis,
  26101. // Min/max for reversed axes
  26102. min: Math.min(selectionMin, selectionMax),
  26103. max: Math.max(selectionMin, selectionMax)
  26104. });
  26105. runZoom = true;
  26106. }
  26107. });
  26108. if (runZoom) {
  26109. fireEvent(chart, 'selection', selectionData, function (args) {
  26110. chart.zoom(extend(args, hasPinched ?
  26111. { animation: false } :
  26112. null));
  26113. });
  26114. }
  26115. }
  26116. if (isNumber(chart.index)) {
  26117. this.selectionMarker = this.selectionMarker.destroy();
  26118. }
  26119. // Reset scaling preview
  26120. if (hasPinched) {
  26121. this.scaleGroups();
  26122. }
  26123. }
  26124. // Reset all. Check isNumber because it may be destroyed on mouse up
  26125. // (#877)
  26126. if (chart && isNumber(chart.index)) {
  26127. css(chart.container, { cursor: chart._cursor });
  26128. chart.cancelClick = this.hasDragged > 10; // #370
  26129. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  26130. this.pinchDown = [];
  26131. }
  26132. }
  26133. /**
  26134. * Finds the closest point to a set of coordinates, using the k-d-tree
  26135. * algorithm.
  26136. *
  26137. * @function Highcharts.Pointer#findNearestKDPoint
  26138. *
  26139. * @param {Array<Highcharts.Series>} series
  26140. * All the series to search in.
  26141. *
  26142. * @param {boolean|undefined} shared
  26143. * Whether it is a shared tooltip or not.
  26144. *
  26145. * @param {Highcharts.PointerEventObject} e
  26146. * The pointer event object, containing chart coordinates of the pointer.
  26147. *
  26148. * @return {Highcharts.Point|undefined}
  26149. * The point closest to given coordinates.
  26150. */
  26151. findNearestKDPoint(series, shared, e) {
  26152. let closest;
  26153. /** @private */
  26154. function sort(p1, p2) {
  26155. const isCloserX = p1.distX - p2.distX, isCloser = p1.dist - p2.dist, isAbove = ((p2.series.group && p2.series.group.zIndex) -
  26156. (p1.series.group && p1.series.group.zIndex));
  26157. let result;
  26158. // We have two points which are not in the same place on xAxis
  26159. // and shared tooltip:
  26160. if (isCloserX !== 0 && shared) { // #5721
  26161. result = isCloserX;
  26162. // Points are not exactly in the same place on x/yAxis:
  26163. }
  26164. else if (isCloser !== 0) {
  26165. result = isCloser;
  26166. // The same xAxis and yAxis position, sort by z-index:
  26167. }
  26168. else if (isAbove !== 0) {
  26169. result = isAbove;
  26170. // The same zIndex, sort by array index:
  26171. }
  26172. else {
  26173. result =
  26174. p1.series.index > p2.series.index ?
  26175. -1 :
  26176. 1;
  26177. }
  26178. return result;
  26179. }
  26180. series.forEach(function (s) {
  26181. const noSharedTooltip = s.noSharedTooltip && shared, compareX = (!noSharedTooltip &&
  26182. s.options.findNearestPointBy.indexOf('y') < 0), point = s.searchPoint(e, compareX);
  26183. if ( // Check that we actually found a point on the series.
  26184. isObject(point, true) && point.series &&
  26185. // Use the new point if it is closer.
  26186. (!isObject(closest, true) ||
  26187. (sort(closest, point) > 0))) {
  26188. closest = point;
  26189. }
  26190. });
  26191. return closest;
  26192. }
  26193. /**
  26194. * @private
  26195. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  26196. */
  26197. getChartCoordinatesFromPoint(point, inverted) {
  26198. const series = point.series, xAxis = series.xAxis, yAxis = series.yAxis, shapeArgs = point.shapeArgs;
  26199. if (xAxis && yAxis) {
  26200. let x = pick(point.clientX, point.plotX);
  26201. let y = point.plotY || 0;
  26202. if (point.isNode &&
  26203. shapeArgs &&
  26204. isNumber(shapeArgs.x) &&
  26205. isNumber(shapeArgs.y)) {
  26206. x = shapeArgs.x;
  26207. y = shapeArgs.y;
  26208. }
  26209. return inverted ? {
  26210. chartX: yAxis.len + yAxis.pos - y,
  26211. chartY: xAxis.len + xAxis.pos - x
  26212. } : {
  26213. chartX: x + xAxis.pos,
  26214. chartY: y + yAxis.pos
  26215. };
  26216. }
  26217. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  26218. // E.g. pies do not have axes
  26219. return {
  26220. chartX: shapeArgs.x,
  26221. chartY: shapeArgs.y
  26222. };
  26223. }
  26224. }
  26225. /**
  26226. * Return the cached chartPosition if it is available on the Pointer,
  26227. * otherwise find it. Running offset is quite expensive, so it should be
  26228. * avoided when we know the chart hasn't moved.
  26229. *
  26230. * @function Highcharts.Pointer#getChartPosition
  26231. *
  26232. * @return {Highcharts.ChartPositionObject}
  26233. * The offset of the chart container within the page
  26234. */
  26235. getChartPosition() {
  26236. if (this.chartPosition) {
  26237. return this.chartPosition;
  26238. }
  26239. const { container } = this.chart;
  26240. const pos = offset(container);
  26241. this.chartPosition = {
  26242. left: pos.left,
  26243. top: pos.top,
  26244. scaleX: 1,
  26245. scaleY: 1
  26246. };
  26247. const offsetWidth = container.offsetWidth;
  26248. const offsetHeight = container.offsetHeight;
  26249. // #13342 - tooltip was not visible in Chrome, when chart
  26250. // updates height.
  26251. if (offsetWidth > 2 && // #13342
  26252. offsetHeight > 2 // #13342
  26253. ) {
  26254. this.chartPosition.scaleX = pos.width / offsetWidth;
  26255. this.chartPosition.scaleY = pos.height / offsetHeight;
  26256. }
  26257. return this.chartPosition;
  26258. }
  26259. /**
  26260. * Get the click position in terms of axis values.
  26261. *
  26262. * @function Highcharts.Pointer#getCoordinates
  26263. *
  26264. * @param {Highcharts.PointerEventObject} e
  26265. * Pointer event, extended with `chartX` and `chartY` properties.
  26266. *
  26267. * @return {Highcharts.PointerAxisCoordinatesObject}
  26268. * Axis coordinates.
  26269. */
  26270. getCoordinates(e) {
  26271. const coordinates = {
  26272. xAxis: [],
  26273. yAxis: []
  26274. };
  26275. this.chart.axes.forEach(function (axis) {
  26276. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  26277. axis: axis,
  26278. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  26279. });
  26280. });
  26281. return coordinates;
  26282. }
  26283. /**
  26284. * Calculates what is the current hovered point/points and series.
  26285. *
  26286. * @private
  26287. * @function Highcharts.Pointer#getHoverData
  26288. *
  26289. * @param {Highcharts.Point|undefined} existingHoverPoint
  26290. * The point currently being hovered.
  26291. *
  26292. * @param {Highcharts.Series|undefined} existingHoverSeries
  26293. * The series currently being hovered.
  26294. *
  26295. * @param {Array<Highcharts.Series>} series
  26296. * All the series in the chart.
  26297. *
  26298. * @param {boolean} isDirectTouch
  26299. * Is the pointer directly hovering the point.
  26300. *
  26301. * @param {boolean|undefined} shared
  26302. * Whether it is a shared tooltip or not.
  26303. *
  26304. * @param {Highcharts.PointerEventObject} [e]
  26305. * The triggering event, containing chart coordinates of the pointer.
  26306. *
  26307. * @return {Object}
  26308. * Object containing resulting hover data: hoverPoint, hoverSeries, and
  26309. * hoverPoints.
  26310. */
  26311. getHoverData(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  26312. const hoverPoints = [], useExisting = !!(isDirectTouch && existingHoverPoint), filter = function (s) {
  26313. return (s.visible &&
  26314. !(!shared && s.directTouch) && // #3821
  26315. pick(s.options.enableMouseTracking, true));
  26316. };
  26317. let hoverSeries = existingHoverSeries,
  26318. // Which series to look in for the hover point
  26319. searchSeries,
  26320. // Parameters needed for beforeGetHoverData event.
  26321. eventArgs = {
  26322. chartX: e ? e.chartX : void 0,
  26323. chartY: e ? e.chartY : void 0,
  26324. shared: shared
  26325. };
  26326. // Find chart.hoverPane and update filter method in polar.
  26327. fireEvent(this, 'beforeGetHoverData', eventArgs);
  26328. const notSticky = hoverSeries && !hoverSeries.stickyTracking;
  26329. searchSeries = notSticky ?
  26330. // Only search on hovered series if it has stickyTracking false
  26331. [hoverSeries] :
  26332. // Filter what series to look in.
  26333. series.filter((s) => s.stickyTracking &&
  26334. (eventArgs.filter || filter)(s));
  26335. // Use existing hovered point or find the one closest to coordinates.
  26336. const hoverPoint = useExisting || !e ?
  26337. existingHoverPoint :
  26338. this.findNearestKDPoint(searchSeries, shared, e);
  26339. // Assign hover series
  26340. hoverSeries = hoverPoint && hoverPoint.series;
  26341. // If we have a hoverPoint, assign hoverPoints.
  26342. if (hoverPoint) {
  26343. // When tooltip is shared, it displays more than one point
  26344. if (shared && !hoverSeries.noSharedTooltip) {
  26345. searchSeries = series.filter(function (s) {
  26346. return eventArgs.filter ?
  26347. eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
  26348. });
  26349. // Get all points with the same x value as the hoverPoint
  26350. searchSeries.forEach(function (s) {
  26351. let point = find(s.points, function (p) {
  26352. return p.x === hoverPoint.x && !p.isNull;
  26353. });
  26354. if (isObject(point)) {
  26355. /*
  26356. * Boost returns a minimal point. Convert it to a usable
  26357. * point for tooltip and states.
  26358. */
  26359. if (s.boosted && s.boost) {
  26360. point = s.boost.getPoint(point);
  26361. }
  26362. hoverPoints.push(point);
  26363. }
  26364. });
  26365. }
  26366. else {
  26367. hoverPoints.push(hoverPoint);
  26368. }
  26369. }
  26370. // Check whether the hoverPoint is inside pane we are hovering over.
  26371. eventArgs = { hoverPoint: hoverPoint };
  26372. fireEvent(this, 'afterGetHoverData', eventArgs);
  26373. return {
  26374. hoverPoint: eventArgs.hoverPoint,
  26375. hoverSeries: hoverSeries,
  26376. hoverPoints: hoverPoints
  26377. };
  26378. }
  26379. /**
  26380. * @private
  26381. * @function Highcharts.Pointer#getPointFromEvent
  26382. */
  26383. getPointFromEvent(e) {
  26384. let target = e.target, point;
  26385. while (target && !point) {
  26386. point = target.point;
  26387. target = target.parentNode;
  26388. }
  26389. return point;
  26390. }
  26391. /**
  26392. * @private
  26393. * @function Highcharts.Pointer#onTrackerMouseOut
  26394. */
  26395. onTrackerMouseOut(e) {
  26396. const chart = this.chart;
  26397. const relatedTarget = e.relatedTarget;
  26398. const series = chart.hoverSeries;
  26399. this.isDirectTouch = false;
  26400. if (series &&
  26401. relatedTarget &&
  26402. !series.stickyTracking &&
  26403. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  26404. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  26405. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  26406. series.onMouseOut();
  26407. }
  26408. }
  26409. /**
  26410. * Utility to detect whether an element has, or has a parent with, a
  26411. * specific class name. Used on detection of tracker objects and on deciding
  26412. * whether hovering the tooltip should cause the active series to mouse out.
  26413. *
  26414. * @function Highcharts.Pointer#inClass
  26415. *
  26416. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  26417. * The element to investigate.
  26418. *
  26419. * @param {string} className
  26420. * The class name to look for.
  26421. *
  26422. * @return {boolean|undefined}
  26423. * True if either the element or one of its parents has the given class
  26424. * name.
  26425. */
  26426. inClass(element, className) {
  26427. let elem = element, elemClassName;
  26428. while (elem) {
  26429. elemClassName = attr(elem, 'class');
  26430. if (elemClassName) {
  26431. if (elemClassName.indexOf(className) !== -1) {
  26432. return true;
  26433. }
  26434. if (elemClassName.indexOf('highcharts-container') !== -1) {
  26435. return false;
  26436. }
  26437. }
  26438. elem = elem.parentElement;
  26439. }
  26440. }
  26441. /**
  26442. * Initialize the Pointer.
  26443. *
  26444. * @private
  26445. * @function Highcharts.Pointer#init
  26446. *
  26447. * @param {Highcharts.Chart} chart
  26448. * The Chart instance.
  26449. *
  26450. * @param {Highcharts.Options} options
  26451. * The root options object. The pointer uses options from the chart and
  26452. * tooltip structures.
  26453. */
  26454. init(chart, options) {
  26455. // Store references
  26456. this.options = options;
  26457. this.chart = chart;
  26458. // Do we need to handle click on a touch device?
  26459. this.runChartClick = Boolean(options.chart.events && options.chart.events.click);
  26460. this.pinchDown = [];
  26461. this.lastValidTouch = {};
  26462. this.setDOMEvents();
  26463. fireEvent(this, 'afterInit');
  26464. }
  26465. /**
  26466. * Takes a browser event object and extends it with custom Highcharts
  26467. * properties `chartX` and `chartY` in order to work on the internal
  26468. * coordinate system.
  26469. *
  26470. * On map charts, the properties `lon` and `lat` are added to the event
  26471. * object given that the chart has projection information.
  26472. *
  26473. * @function Highcharts.Pointer#normalize
  26474. *
  26475. * @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
  26476. * Event object in standard browsers.
  26477. *
  26478. * @param {Highcharts.OffsetObject} [chartPosition]
  26479. * Additional chart offset.
  26480. *
  26481. * @return {Highcharts.PointerEventObject}
  26482. * A browser event with extended properties `chartX` and `chartY`.
  26483. */
  26484. normalize(e, chartPosition) {
  26485. const touches = e.touches;
  26486. // iOS (#2757)
  26487. const ePos = (touches ?
  26488. touches.length ?
  26489. touches.item(0) :
  26490. (pick(// #13534
  26491. touches.changedTouches, e.changedTouches))[0] :
  26492. e);
  26493. // Get mouse position
  26494. if (!chartPosition) {
  26495. chartPosition = this.getChartPosition();
  26496. }
  26497. let chartX = ePos.pageX - chartPosition.left, chartY = ePos.pageY - chartPosition.top;
  26498. // #11329 - when there is scaling on a parent element, we need to take
  26499. // this into account
  26500. chartX /= chartPosition.scaleX;
  26501. chartY /= chartPosition.scaleY;
  26502. return extend(e, {
  26503. chartX: Math.round(chartX),
  26504. chartY: Math.round(chartY)
  26505. });
  26506. }
  26507. /**
  26508. * @private
  26509. * @function Highcharts.Pointer#onContainerClick
  26510. */
  26511. onContainerClick(e) {
  26512. const chart = this.chart;
  26513. const hoverPoint = chart.hoverPoint;
  26514. const pEvt = this.normalize(e);
  26515. const plotLeft = chart.plotLeft;
  26516. const plotTop = chart.plotTop;
  26517. if (!chart.cancelClick) {
  26518. // On tracker click, fire the series and point events. #783, #1583
  26519. if (hoverPoint &&
  26520. this.inClass(pEvt.target, 'highcharts-tracker')) {
  26521. // the series click event
  26522. fireEvent(hoverPoint.series, 'click', extend(pEvt, {
  26523. point: hoverPoint
  26524. }));
  26525. // the point click event
  26526. if (chart.hoverPoint) { // it may be destroyed (#1844)
  26527. hoverPoint.firePointEvent('click', pEvt);
  26528. }
  26529. // When clicking outside a tracker, fire a chart event
  26530. }
  26531. else {
  26532. extend(pEvt, this.getCoordinates(pEvt));
  26533. // fire a click event in the chart
  26534. if (chart.isInsidePlot(pEvt.chartX - plotLeft, pEvt.chartY - plotTop, {
  26535. visiblePlotOnly: true
  26536. })) {
  26537. fireEvent(chart, 'click', pEvt);
  26538. }
  26539. }
  26540. }
  26541. }
  26542. /**
  26543. * @private
  26544. * @function Highcharts.Pointer#onContainerMouseDown
  26545. */
  26546. onContainerMouseDown(e) {
  26547. const isPrimaryButton = ((e.buttons || e.button) & 1) === 1;
  26548. e = this.normalize(e);
  26549. // #11635, Firefox does not reliably fire move event after click scroll
  26550. if (H.isFirefox &&
  26551. e.button !== 0) {
  26552. this.onContainerMouseMove(e);
  26553. }
  26554. // #11635, limiting to primary button
  26555. if (typeof e.button === 'undefined' ||
  26556. isPrimaryButton) {
  26557. this.zoomOption(e);
  26558. // #295, #13737 solve conflict between container drag and chart zoom
  26559. if (isPrimaryButton &&
  26560. e.preventDefault) {
  26561. e.preventDefault();
  26562. }
  26563. this.dragStart(e);
  26564. }
  26565. }
  26566. /**
  26567. * When mouse leaves the container, hide the tooltip.
  26568. * @private
  26569. * @function Highcharts.Pointer#onContainerMouseLeave
  26570. */
  26571. onContainerMouseLeave(e) {
  26572. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26573. e = this.normalize(e);
  26574. // #4886, MS Touch end fires mouseleave but with no related target
  26575. if (chart &&
  26576. e.relatedTarget &&
  26577. !this.inClass(e.relatedTarget, 'highcharts-tooltip')) {
  26578. chart.pointer.reset();
  26579. // Also reset the chart position, used in #149 fix
  26580. chart.pointer.chartPosition = void 0;
  26581. }
  26582. }
  26583. /**
  26584. * When mouse enters the container, delete pointer's chartPosition.
  26585. * @private
  26586. * @function Highcharts.Pointer#onContainerMouseEnter
  26587. */
  26588. onContainerMouseEnter(e) {
  26589. delete this.chartPosition;
  26590. }
  26591. /**
  26592. * The mousemove, touchmove and touchstart event handler
  26593. * @private
  26594. * @function Highcharts.Pointer#onContainerMouseMove
  26595. */
  26596. onContainerMouseMove(e) {
  26597. const chart = this.chart, tooltip = chart.tooltip, pEvt = this.normalize(e);
  26598. this.setHoverChartIndex();
  26599. if (chart.mouseIsDown === 'mousedown' || this.touchSelect(pEvt)) {
  26600. this.drag(pEvt);
  26601. }
  26602. // Show the tooltip and run mouse over events (#977)
  26603. if (!chart.openMenu &&
  26604. (this.inClass(pEvt.target, 'highcharts-tracker') ||
  26605. chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26606. visiblePlotOnly: true
  26607. })) &&
  26608. // If the tooltip has stickOnContact enabled, do nothing. This
  26609. // applies regardless of any combinations of the `split` and
  26610. // `useHTML` options.
  26611. !(tooltip &&
  26612. tooltip.shouldStickOnContact(pEvt))) {
  26613. if (this.inClass(pEvt.target, 'highcharts-no-tooltip')) {
  26614. this.reset(false, 0);
  26615. }
  26616. else {
  26617. this.runPointActions(pEvt);
  26618. }
  26619. }
  26620. }
  26621. /**
  26622. * @private
  26623. * @function Highcharts.Pointer#onDocumentTouchEnd
  26624. */
  26625. onDocumentTouchEnd(e) {
  26626. const hoverChart = charts[pick(Pointer.hoverChartIndex, -1)];
  26627. if (hoverChart) {
  26628. hoverChart.pointer.drop(e);
  26629. }
  26630. }
  26631. /**
  26632. * @private
  26633. * @function Highcharts.Pointer#onContainerTouchMove
  26634. */
  26635. onContainerTouchMove(e) {
  26636. if (this.touchSelect(e)) {
  26637. this.onContainerMouseMove(e);
  26638. }
  26639. else {
  26640. this.touch(e);
  26641. }
  26642. }
  26643. /**
  26644. * @private
  26645. * @function Highcharts.Pointer#onContainerTouchStart
  26646. */
  26647. onContainerTouchStart(e) {
  26648. if (this.touchSelect(e)) {
  26649. this.onContainerMouseDown(e);
  26650. }
  26651. else {
  26652. this.zoomOption(e);
  26653. this.touch(e, true);
  26654. }
  26655. }
  26656. /**
  26657. * Special handler for mouse move that will hide the tooltip when the mouse
  26658. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  26659. * always fire.
  26660. * @private
  26661. * @function Highcharts.Pointer#onDocumentMouseMove
  26662. */
  26663. onDocumentMouseMove(e) {
  26664. const chart = this.chart;
  26665. const tooltip = chart.tooltip;
  26666. const chartPosition = this.chartPosition;
  26667. const pEvt = this.normalize(e, chartPosition);
  26668. // If we're outside, hide the tooltip
  26669. if (chartPosition &&
  26670. !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26671. visiblePlotOnly: true
  26672. }) &&
  26673. !(tooltip &&
  26674. tooltip.shouldStickOnContact(pEvt)) &&
  26675. !this.inClass(pEvt.target, 'highcharts-tracker')) {
  26676. this.reset();
  26677. }
  26678. }
  26679. /**
  26680. * @private
  26681. * @function Highcharts.Pointer#onDocumentMouseUp
  26682. */
  26683. onDocumentMouseUp(e) {
  26684. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26685. if (chart) {
  26686. chart.pointer.drop(e);
  26687. }
  26688. }
  26689. /**
  26690. * Handle touch events with two touches
  26691. * @private
  26692. * @function Highcharts.Pointer#pinch
  26693. */
  26694. pinch(e) {
  26695. const self = this, chart = self.chart, pinchDown = self.pinchDown, touches = (e.touches || []), touchesLength = touches.length, lastValidTouch = self.lastValidTouch, hasZoom = self.hasZoom, transform = {}, fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, 'highcharts-tracker') &&
  26696. chart.runTrackerClick) ||
  26697. self.runChartClick), clip = {}, tooltip = self.chart.tooltip, followTouchMove = touchesLength === 1 &&
  26698. pick((tooltip && tooltip.options.followTouchMove), true);
  26699. let selectionMarker = self.selectionMarker;
  26700. // Don't initiate panning until the user has pinched. This prevents us
  26701. // from blocking page scrolling as users scroll down a long page
  26702. // (#4210).
  26703. if (touchesLength > 1) {
  26704. self.initiated = true;
  26705. }
  26706. else if (followTouchMove) {
  26707. // #16119: Prevent blocking scroll when single-finger panning is
  26708. // not enabled
  26709. self.initiated = false;
  26710. }
  26711. // On touch devices, only proceed to trigger click if a handler is
  26712. // defined
  26713. if (hasZoom &&
  26714. self.initiated &&
  26715. !fireClickEvent &&
  26716. e.cancelable !== false) {
  26717. e.preventDefault();
  26718. }
  26719. // Normalize each touch
  26720. [].map.call(touches, function (e) {
  26721. return self.normalize(e);
  26722. });
  26723. // Register the touch start position
  26724. if (e.type === 'touchstart') {
  26725. [].forEach.call(touches, function (e, i) {
  26726. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  26727. });
  26728. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  26729. pinchDown[1].chartX];
  26730. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  26731. pinchDown[1].chartY];
  26732. // Identify the data bounds in pixels
  26733. chart.axes.forEach(function (axis) {
  26734. if (axis.zoomEnabled) {
  26735. const bounds = chart.bounds[axis.horiz ? 'h' : 'v'], minPixelPadding = axis.minPixelPadding, min = axis.toPixels(Math.min(pick(axis.options.min, axis.dataMin), axis.dataMin)), max = axis.toPixels(Math.max(pick(axis.options.max, axis.dataMax), axis.dataMax)), absMin = Math.min(min, max), absMax = Math.max(min, max);
  26736. // Store the bounds for use in the touchmove handler
  26737. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  26738. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  26739. }
  26740. });
  26741. self.res = true; // reset on next move
  26742. // Optionally move the tooltip on touchmove
  26743. }
  26744. else if (followTouchMove) {
  26745. this.runPointActions(self.normalize(e));
  26746. // Event type is touchmove, handle panning and pinching
  26747. }
  26748. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  26749. // fires first
  26750. fireEvent(chart, 'touchpan', { originalEvent: e }, () => {
  26751. // Set the marker
  26752. if (!selectionMarker) {
  26753. // @todo It's a mock object, so maybe we need a separate
  26754. // interface
  26755. self.selectionMarker = selectionMarker = extend({
  26756. destroy: noop,
  26757. touch: true
  26758. }, chart.plotBox);
  26759. }
  26760. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26761. self.hasPinched = hasZoom;
  26762. // Scale and translate the groups to provide visual feedback
  26763. // during pinching
  26764. self.scaleGroups(transform, clip);
  26765. });
  26766. if (self.res) {
  26767. self.res = false;
  26768. this.reset(false, 0);
  26769. }
  26770. }
  26771. }
  26772. /**
  26773. * Run translation operations
  26774. * @private
  26775. * @function Highcharts.Pointer#pinchTranslate
  26776. */
  26777. pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  26778. if (this.zoomHor) {
  26779. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26780. }
  26781. if (this.zoomVert) {
  26782. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26783. }
  26784. }
  26785. /**
  26786. * Run translation operations for each direction (horizontal and vertical)
  26787. * independently.
  26788. * @private
  26789. * @function Highcharts.Pointer#pinchTranslateDirection
  26790. */
  26791. pinchTranslateDirection(horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  26792. const chart = this.chart, xy = horiz ? 'x' : 'y', XY = horiz ? 'X' : 'Y', sChartXY = ('chart' + XY), wh = horiz ? 'width' : 'height', plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], inverted = chart.inverted, bounds = chart.bounds[horiz ? 'h' : 'v'], singleTouch = pinchDown.length === 1, touch0Start = pinchDown[0][sChartXY], touch1Start = !singleTouch && pinchDown[1][sChartXY], setScale = function () {
  26793. // Don't zoom if fingers are too close on this axis
  26794. if (typeof touch1Now === 'number' &&
  26795. Math.abs(touch0Start - touch1Start) > 20) {
  26796. scale = forcedScale ||
  26797. Math.abs(touch0Now - touch1Now) /
  26798. Math.abs(touch0Start - touch1Start);
  26799. }
  26800. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  26801. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  26802. };
  26803. let selectionWH, selectionXY, clipXY, scale = forcedScale || 1, touch0Now = touches[0][sChartXY], touch1Now = !singleTouch && touches[1][sChartXY], outOfBounds;
  26804. // Set the scale, first pass
  26805. setScale();
  26806. // The clip position (x or y) is altered if out of bounds, the selection
  26807. // position is not
  26808. selectionXY = clipXY;
  26809. // Out of bounds
  26810. if (selectionXY < bounds.min) {
  26811. selectionXY = bounds.min;
  26812. outOfBounds = true;
  26813. }
  26814. else if (selectionXY + selectionWH > bounds.max) {
  26815. selectionXY = bounds.max - selectionWH;
  26816. outOfBounds = true;
  26817. }
  26818. // Is the chart dragged off its bounds, determined by dataMin and
  26819. // dataMax?
  26820. if (outOfBounds) {
  26821. // Modify the touchNow position in order to create an elastic drag
  26822. // movement. This indicates to the user that the chart is responsive
  26823. // but can't be dragged further.
  26824. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  26825. if (typeof touch1Now === 'number') {
  26826. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  26827. }
  26828. // Set the scale, second pass to adapt to the modified touchNow
  26829. // positions
  26830. setScale();
  26831. }
  26832. else {
  26833. lastValidTouch[xy] = [touch0Now, touch1Now];
  26834. }
  26835. // Set geometry for clipping, selection and transformation
  26836. if (!inverted) {
  26837. clip[xy] = clipXY - plotLeftTop;
  26838. clip[wh] = selectionWH;
  26839. }
  26840. const scaleKey = inverted ?
  26841. (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  26842. const transformScale = inverted ? 1 / scale : scale;
  26843. selectionMarker[wh] = selectionWH;
  26844. selectionMarker[xy] = selectionXY;
  26845. transform[scaleKey] = scale;
  26846. transform['translate' + XY] = (transformScale * plotLeftTop) +
  26847. (touch0Now - (transformScale * touch0Start));
  26848. }
  26849. /**
  26850. * Reset the tracking by hiding the tooltip, the hover series state and the
  26851. * hover point
  26852. *
  26853. * @function Highcharts.Pointer#reset
  26854. *
  26855. * @param {boolean} [allowMove]
  26856. * Instead of destroying the tooltip altogether, allow moving it if
  26857. * possible.
  26858. *
  26859. * @param {number} [delay]
  26860. */
  26861. reset(allowMove, delay) {
  26862. const pointer = this, chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, hoverPoints = chart.hoverPoints, tooltip = chart.tooltip, tooltipPoints = tooltip && tooltip.shared ?
  26863. hoverPoints :
  26864. hoverPoint;
  26865. // Check if the points have moved outside the plot area (#1003, #4736,
  26866. // #5101)
  26867. if (allowMove && tooltipPoints) {
  26868. splat(tooltipPoints).forEach(function (point) {
  26869. if (point.series.isCartesian &&
  26870. typeof point.plotX === 'undefined') {
  26871. allowMove = false;
  26872. }
  26873. });
  26874. }
  26875. // Just move the tooltip, #349
  26876. if (allowMove) {
  26877. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  26878. tooltip.refresh(tooltipPoints);
  26879. if (tooltip.shared && hoverPoints) { // #8284
  26880. hoverPoints.forEach(function (point) {
  26881. point.setState(point.state, true);
  26882. if (point.series.isCartesian) {
  26883. if (point.series.xAxis.crosshair) {
  26884. point.series.xAxis
  26885. .drawCrosshair(null, point);
  26886. }
  26887. if (point.series.yAxis.crosshair) {
  26888. point.series.yAxis
  26889. .drawCrosshair(null, point);
  26890. }
  26891. }
  26892. });
  26893. }
  26894. else if (hoverPoint) { // #2500
  26895. hoverPoint.setState(hoverPoint.state, true);
  26896. chart.axes.forEach(function (axis) {
  26897. if (axis.crosshair &&
  26898. hoverPoint.series[axis.coll] === axis) {
  26899. axis.drawCrosshair(null, hoverPoint);
  26900. }
  26901. });
  26902. }
  26903. }
  26904. // Full reset
  26905. }
  26906. else {
  26907. if (hoverPoint) {
  26908. hoverPoint.onMouseOut();
  26909. }
  26910. if (hoverPoints) {
  26911. hoverPoints.forEach(function (point) {
  26912. point.setState();
  26913. });
  26914. }
  26915. if (hoverSeries) {
  26916. hoverSeries.onMouseOut();
  26917. }
  26918. if (tooltip) {
  26919. tooltip.hide(delay);
  26920. }
  26921. if (pointer.unDocMouseMove) {
  26922. pointer.unDocMouseMove = pointer.unDocMouseMove();
  26923. }
  26924. // Remove crosshairs
  26925. chart.axes.forEach(function (axis) {
  26926. axis.hideCrosshair();
  26927. });
  26928. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  26929. }
  26930. }
  26931. /**
  26932. * With line type charts with a single tracker, get the point closest to the
  26933. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  26934. *
  26935. * @private
  26936. * @function Highcharts.Pointer#runPointActions
  26937. *
  26938. * @emits Highcharts.Point#event:mouseOut
  26939. * @emits Highcharts.Point#event:mouseOver
  26940. */
  26941. runPointActions(e, p, force) {
  26942. const pointer = this, chart = pointer.chart, series = chart.series, tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  26943. chart.tooltip :
  26944. void 0), shared = (tooltip ?
  26945. tooltip.shared :
  26946. false);
  26947. let hoverPoint = p || chart.hoverPoint, hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries;
  26948. const // onMouseOver or already hovering a series with directTouch
  26949. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  26950. pointer.isDirectTouch)), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e);
  26951. // Update variables from hoverData.
  26952. hoverPoint = hoverData.hoverPoint;
  26953. hoverSeries = hoverData.hoverSeries;
  26954. const points = hoverData.hoverPoints, followPointer = hoverSeries &&
  26955. hoverSeries.tooltipOptions.followPointer &&
  26956. !hoverSeries.tooltipOptions.split, useSharedTooltip = (shared &&
  26957. hoverSeries &&
  26958. !hoverSeries.noSharedTooltip);
  26959. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  26960. // #3926, #4200
  26961. if (hoverPoint &&
  26962. (force ||
  26963. hoverPoint !== chart.hoverPoint ||
  26964. (tooltip && tooltip.isHidden))) {
  26965. (chart.hoverPoints || []).forEach(function (p) {
  26966. if (points.indexOf(p) === -1) {
  26967. p.setState();
  26968. }
  26969. });
  26970. // Set normal state to previous series
  26971. if (chart.hoverSeries !== hoverSeries) {
  26972. hoverSeries.onMouseOver();
  26973. }
  26974. pointer.applyInactiveState(points);
  26975. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  26976. (points || []).forEach(function (p) {
  26977. p.setState('hover');
  26978. });
  26979. // If tracking is on series in stead of on each point,
  26980. // fire mouseOver on hover point. // #4448
  26981. if (chart.hoverPoint) {
  26982. chart.hoverPoint.firePointEvent('mouseOut');
  26983. }
  26984. // Hover point may have been destroyed in the event handlers (#7127)
  26985. if (!hoverPoint.series) {
  26986. return;
  26987. }
  26988. /**
  26989. * Contains all hovered points.
  26990. *
  26991. * @name Highcharts.Chart#hoverPoints
  26992. * @type {Array<Highcharts.Point>|null}
  26993. */
  26994. chart.hoverPoints = points;
  26995. /**
  26996. * Contains the original hovered point.
  26997. *
  26998. * @name Highcharts.Chart#hoverPoint
  26999. * @type {Highcharts.Point|null}
  27000. */
  27001. chart.hoverPoint = hoverPoint;
  27002. /**
  27003. * Hover state should not be lost when axis is updated (#12569)
  27004. * Axis.update runs pointer.reset which uses chart.hoverPoint.state
  27005. * to apply state which does not exist in hoverPoint yet.
  27006. * The mouseOver event should be triggered when hoverPoint
  27007. * is correct.
  27008. */
  27009. hoverPoint.firePointEvent('mouseOver', void 0, () => {
  27010. // Draw tooltip if necessary
  27011. if (tooltip && hoverPoint) {
  27012. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  27013. }
  27014. });
  27015. // Update positions (regardless of kdpoint or hoverPoint)
  27016. }
  27017. else if (followPointer && tooltip && !tooltip.isHidden) {
  27018. const anchor = tooltip.getAnchor([{}], e);
  27019. if (chart.isInsidePlot(anchor[0], anchor[1], {
  27020. visiblePlotOnly: true
  27021. })) {
  27022. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  27023. }
  27024. }
  27025. // Start the event listener to pick up the tooltip and crosshairs
  27026. if (!pointer.unDocMouseMove) {
  27027. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  27028. const chart = charts[Pointer.hoverChartIndex];
  27029. if (chart) {
  27030. chart.pointer.onDocumentMouseMove(e);
  27031. }
  27032. });
  27033. pointer.eventsToUnbind.push(pointer.unDocMouseMove);
  27034. }
  27035. // Issues related to crosshair #4927, #5269 #5066, #5658
  27036. chart.axes.forEach(function drawAxisCrosshair(axis) {
  27037. const snap = pick((axis.crosshair || {}).snap, true);
  27038. let point;
  27039. if (snap) {
  27040. point = chart.hoverPoint; // #13002
  27041. if (!point || point.series[axis.coll] !== axis) {
  27042. point = find(points, (p) => p.series && p.series[axis.coll] === axis);
  27043. }
  27044. }
  27045. // Axis has snapping crosshairs, and one of the hover points belongs
  27046. // to axis. Always call drawCrosshair when it is not snap.
  27047. if (point || !snap) {
  27048. axis.drawCrosshair(e, point);
  27049. // Axis has snapping crosshairs, but no hover point belongs to axis
  27050. }
  27051. else {
  27052. axis.hideCrosshair();
  27053. }
  27054. });
  27055. }
  27056. /**
  27057. * Scale series groups to a certain scale and translation.
  27058. * @private
  27059. * @function Highcharts.Pointer#scaleGroups
  27060. */
  27061. scaleGroups(attribs, clip) {
  27062. const chart = this.chart;
  27063. // Scale each series
  27064. chart.series.forEach(function (series) {
  27065. const seriesAttribs = attribs || series.getPlotBox(); // #1701
  27066. if (series.group &&
  27067. ((series.xAxis && series.xAxis.zoomEnabled) ||
  27068. chart.mapView)) {
  27069. series.group.attr(seriesAttribs);
  27070. if (series.markerGroup) {
  27071. series.markerGroup.attr(seriesAttribs);
  27072. series.markerGroup.clip(clip ? chart.clipRect : null);
  27073. }
  27074. if (series.dataLabelsGroup) {
  27075. series.dataLabelsGroup.attr(seriesAttribs);
  27076. }
  27077. }
  27078. });
  27079. // Clip
  27080. chart.clipRect.attr(clip || chart.clipBox);
  27081. }
  27082. /**
  27083. * Set the JS DOM events on the container and document. This method should
  27084. * contain a one-to-one assignment between methods and their handlers. Any
  27085. * advanced logic should be moved to the handler reflecting the event's
  27086. * name.
  27087. * @private
  27088. * @function Highcharts.Pointer#setDOMEvents
  27089. */
  27090. setDOMEvents() {
  27091. const container = this.chart.container, ownerDoc = container.ownerDocument;
  27092. container.onmousedown = this.onContainerMouseDown.bind(this);
  27093. container.onmousemove = this.onContainerMouseMove.bind(this);
  27094. container.onclick = this.onContainerClick.bind(this);
  27095. this.eventsToUnbind.push(addEvent(container, 'mouseenter', this.onContainerMouseEnter.bind(this)));
  27096. this.eventsToUnbind.push(addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this)));
  27097. if (!Pointer.unbindDocumentMouseUp) {
  27098. Pointer.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this));
  27099. }
  27100. // In case we are dealing with overflow, reset the chart position when
  27101. // scrolling parent elements
  27102. let parent = this.chart.renderTo.parentElement;
  27103. while (parent && parent.tagName !== 'BODY') {
  27104. this.eventsToUnbind.push(addEvent(parent, 'scroll', () => {
  27105. delete this.chartPosition;
  27106. }));
  27107. parent = parent.parentElement;
  27108. }
  27109. if (H.hasTouch) {
  27110. this.eventsToUnbind.push(addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this), { passive: false }));
  27111. this.eventsToUnbind.push(addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this), { passive: false }));
  27112. if (!Pointer.unbindDocumentTouchEnd) {
  27113. Pointer.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this), { passive: false });
  27114. }
  27115. }
  27116. }
  27117. /**
  27118. * Sets the index of the hovered chart and leaves the previous hovered
  27119. * chart, to reset states like tooltip.
  27120. * @private
  27121. * @function Highcharts.Pointer#setHoverChartIndex
  27122. */
  27123. setHoverChartIndex() {
  27124. const chart = this.chart;
  27125. const hoverChart = H.charts[pick(Pointer.hoverChartIndex, -1)];
  27126. if (hoverChart &&
  27127. hoverChart !== chart) {
  27128. hoverChart.pointer.onContainerMouseLeave({ relatedTarget: chart.container });
  27129. }
  27130. if (!hoverChart ||
  27131. !hoverChart.mouseIsDown) {
  27132. Pointer.hoverChartIndex = chart.index;
  27133. }
  27134. }
  27135. /**
  27136. * General touch handler shared by touchstart and touchmove.
  27137. * @private
  27138. * @function Highcharts.Pointer#touch
  27139. */
  27140. touch(e, start) {
  27141. const chart = this.chart;
  27142. let hasMoved, pinchDown, isInside;
  27143. this.setHoverChartIndex();
  27144. if (e.touches.length === 1) {
  27145. e = this.normalize(e);
  27146. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop, {
  27147. visiblePlotOnly: true
  27148. });
  27149. if (isInside && !chart.openMenu) {
  27150. // Run mouse events and display tooltip etc
  27151. if (start) {
  27152. this.runPointActions(e);
  27153. }
  27154. // Android fires touchmove events after the touchstart even if
  27155. // the finger hasn't moved, or moved only a pixel or two. In iOS
  27156. // however, the touchmove doesn't fire unless the finger moves
  27157. // more than ~4px. So we emulate this behaviour in Android by
  27158. // checking how much it moved, and cancelling on small
  27159. // distances. #3450.
  27160. if (e.type === 'touchmove') {
  27161. pinchDown = this.pinchDown;
  27162. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  27163. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  27164. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  27165. }
  27166. if (pick(hasMoved, true)) {
  27167. this.pinch(e);
  27168. }
  27169. }
  27170. else if (start) {
  27171. // Hide the tooltip on touching outside the plot area (#1203)
  27172. this.reset();
  27173. }
  27174. }
  27175. else if (e.touches.length === 2) {
  27176. this.pinch(e);
  27177. }
  27178. }
  27179. /**
  27180. * Returns true if the chart is set up for zooming by single touch and the
  27181. * event is capable
  27182. * @private
  27183. * @function Highcharts.Pointer#touchSelect
  27184. */
  27185. touchSelect(e) {
  27186. return Boolean(this.chart.zooming.singleTouch &&
  27187. e.touches &&
  27188. e.touches.length === 1);
  27189. }
  27190. /**
  27191. * Resolve the zoomType option, this is reset on all touch start and mouse
  27192. * down events.
  27193. * @private
  27194. * @function Highcharts.Pointer#zoomOption
  27195. */
  27196. zoomOption(e) {
  27197. const chart = this.chart, options = chart.options.chart, inverted = chart.inverted;
  27198. let zoomType = chart.zooming.type || '', zoomX, zoomY;
  27199. // Look for the pinchType option
  27200. if (/touch/.test(e.type)) {
  27201. zoomType = pick(chart.zooming.pinchType, zoomType);
  27202. }
  27203. this.zoomX = zoomX = /x/.test(zoomType);
  27204. this.zoomY = zoomY = /y/.test(zoomType);
  27205. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  27206. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  27207. this.hasZoom = zoomX || zoomY;
  27208. }
  27209. }
  27210. /* *
  27211. *
  27212. * Class Namespace
  27213. *
  27214. * */
  27215. (function (Pointer) {
  27216. /* *
  27217. *
  27218. * Declarations
  27219. *
  27220. * */
  27221. /* *
  27222. *
  27223. * Constants
  27224. *
  27225. * */
  27226. const composedEvents = [];
  27227. const composedMembers = [];
  27228. /* *
  27229. *
  27230. * Functions
  27231. *
  27232. * */
  27233. /**
  27234. * @private
  27235. */
  27236. function compose(ChartClass) {
  27237. if (U.pushUnique(composedMembers, ChartClass)) {
  27238. addEvent(ChartClass, 'beforeRender', function () {
  27239. /**
  27240. * The Pointer that keeps track of mouse and touch
  27241. * interaction.
  27242. *
  27243. * @memberof Highcharts.Chart
  27244. * @name pointer
  27245. * @type {Highcharts.Pointer}
  27246. * @instance
  27247. */
  27248. this.pointer = new Pointer(this, this.options);
  27249. });
  27250. }
  27251. }
  27252. Pointer.compose = compose;
  27253. /**
  27254. * @private
  27255. */
  27256. function dissolve() {
  27257. for (let i = 0, iEnd = composedEvents.length; i < iEnd; ++i) {
  27258. composedEvents[i]();
  27259. }
  27260. composedEvents.length = 0;
  27261. }
  27262. Pointer.dissolve = dissolve;
  27263. })(Pointer || (Pointer = {}));
  27264. /* *
  27265. *
  27266. * Default Export
  27267. *
  27268. * */
  27269. /* *
  27270. *
  27271. * API Declarations
  27272. *
  27273. * */
  27274. /**
  27275. * Chart position and scale.
  27276. *
  27277. * @interface Highcharts.ChartPositionObject
  27278. */ /**
  27279. * @name Highcharts.ChartPositionObject#left
  27280. * @type {number}
  27281. */ /**
  27282. * @name Highcharts.ChartPositionObject#scaleX
  27283. * @type {number}
  27284. */ /**
  27285. * @name Highcharts.ChartPositionObject#scaleY
  27286. * @type {number}
  27287. */ /**
  27288. * @name Highcharts.ChartPositionObject#top
  27289. * @type {number}
  27290. */
  27291. /**
  27292. * One position in relation to an axis.
  27293. *
  27294. * @interface Highcharts.PointerAxisCoordinateObject
  27295. */ /**
  27296. * Related axis.
  27297. *
  27298. * @name Highcharts.PointerAxisCoordinateObject#axis
  27299. * @type {Highcharts.Axis}
  27300. */ /**
  27301. * Axis value.
  27302. *
  27303. * @name Highcharts.PointerAxisCoordinateObject#value
  27304. * @type {number}
  27305. */
  27306. /**
  27307. * Positions in terms of axis values.
  27308. *
  27309. * @interface Highcharts.PointerAxisCoordinatesObject
  27310. */ /**
  27311. * Positions on the x-axis.
  27312. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  27313. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27314. */ /**
  27315. * Positions on the y-axis.
  27316. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  27317. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27318. */
  27319. /**
  27320. * Pointer coordinates.
  27321. *
  27322. * @interface Highcharts.PointerCoordinatesObject
  27323. */ /**
  27324. * @name Highcharts.PointerCoordinatesObject#chartX
  27325. * @type {number}
  27326. */ /**
  27327. * @name Highcharts.PointerCoordinatesObject#chartY
  27328. * @type {number}
  27329. */
  27330. /**
  27331. * A native browser mouse or touch event, extended with position information
  27332. * relative to the {@link Chart.container}.
  27333. *
  27334. * @interface Highcharts.PointerEventObject
  27335. * @extends global.PointerEvent
  27336. */ /**
  27337. * The X coordinate of the pointer interaction relative to the chart.
  27338. *
  27339. * @name Highcharts.PointerEventObject#chartX
  27340. * @type {number}
  27341. */ /**
  27342. * The Y coordinate of the pointer interaction relative to the chart.
  27343. *
  27344. * @name Highcharts.PointerEventObject#chartY
  27345. * @type {number}
  27346. */
  27347. /**
  27348. * Axis-specific data of a selection.
  27349. *
  27350. * @interface Highcharts.SelectDataObject
  27351. */ /**
  27352. * The selected Axis.
  27353. * @name Highcharts.SelectDataObject#axis
  27354. * @type {Highcharts.Axis}
  27355. */ /**
  27356. * The maximum axis value, either automatic or set manually.
  27357. * @name Highcharts.SelectDataObject#max
  27358. * @type {number}
  27359. */ /**
  27360. * The minimum axis value, either automatic or set manually.
  27361. * @name Highcharts.SelectDataObject#min
  27362. * @type {number}
  27363. */
  27364. /**
  27365. * Object for select events.
  27366. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  27367. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  27368. *
  27369. * @interface Highcharts.SelectEventObject
  27370. */ /**
  27371. * The related browser event.
  27372. * @name Highcharts.SelectEventObject#originalEvent
  27373. * @type {global.Event}
  27374. */ /**
  27375. * Indicates a reset event to restore default state.
  27376. * @name Highcharts.SelectEventObject#resetSelection
  27377. * @type {boolean|undefined}
  27378. */ /**
  27379. * Arrays containing the axes of each dimension and each axis' min and max
  27380. * values.
  27381. * @name Highcharts.SelectEventObject#xAxis
  27382. * @type {Array<Highcharts.SelectDataObject>}
  27383. */ /**
  27384. * Arrays containing the axes of each dimension and each axis' min and max
  27385. * values.
  27386. * @name Highcharts.SelectEventObject#yAxis
  27387. * @type {Array<Highcharts.SelectDataObject>}
  27388. */
  27389. ''; // keeps doclets above in JS file
  27390. return Pointer;
  27391. });
  27392. _registerModule(_modules, 'Core/Legend/Legend.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Utilities.js']], function (A, F, H, Point, R, U) {
  27393. /* *
  27394. *
  27395. * (c) 2010-2021 Torstein Honsi
  27396. *
  27397. * License: www.highcharts.com/license
  27398. *
  27399. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27400. *
  27401. * */
  27402. const { animObject, setAnimation } = A;
  27403. const { format } = F;
  27404. const { marginNames } = H;
  27405. const { distribute } = R;
  27406. const { addEvent, createElement, css, defined, discardElement, find, fireEvent, isNumber, merge, pick, relativeLength, stableSort, syncTimeout } = U;
  27407. /* *
  27408. *
  27409. * Class
  27410. *
  27411. * */
  27412. /**
  27413. * The overview of the chart's series. The legend object is instanciated
  27414. * internally in the chart constructor, and is available from the `chart.legend`
  27415. * property. Each chart has only one legend.
  27416. *
  27417. * @class
  27418. * @name Highcharts.Legend
  27419. *
  27420. * @param {Highcharts.Chart} chart
  27421. * The chart instance.
  27422. *
  27423. * @param {Highcharts.LegendOptions} options
  27424. * Legend options.
  27425. */
  27426. class Legend {
  27427. /* *
  27428. *
  27429. * Constructors
  27430. *
  27431. * */
  27432. constructor(chart, options) {
  27433. /* *
  27434. *
  27435. * Properties
  27436. *
  27437. * */
  27438. this.allItems = [];
  27439. this.box = void 0;
  27440. this.contentGroup = void 0;
  27441. this.display = false;
  27442. this.group = void 0;
  27443. this.initialItemY = 0;
  27444. this.itemHeight = 0;
  27445. this.itemMarginBottom = 0;
  27446. this.itemMarginTop = 0;
  27447. this.itemX = 0;
  27448. this.itemY = 0;
  27449. this.lastItemY = 0;
  27450. this.lastLineHeight = 0;
  27451. this.legendHeight = 0;
  27452. this.legendWidth = 0;
  27453. this.maxItemWidth = 0;
  27454. this.maxLegendWidth = 0;
  27455. this.offsetWidth = 0;
  27456. this.options = void 0;
  27457. this.padding = 0;
  27458. this.pages = [];
  27459. this.proximate = false;
  27460. this.scrollGroup = void 0;
  27461. this.symbolHeight = 0;
  27462. this.symbolWidth = 0;
  27463. this.titleHeight = 0;
  27464. this.totalItemWidth = 0;
  27465. this.widthOption = 0;
  27466. this.chart = chart;
  27467. this.init(chart, options);
  27468. }
  27469. /* *
  27470. *
  27471. * Functions
  27472. *
  27473. * */
  27474. /**
  27475. * Initialize the legend.
  27476. *
  27477. * @private
  27478. * @function Highcharts.Legend#init
  27479. *
  27480. * @param {Highcharts.Chart} chart
  27481. * The chart instance.
  27482. *
  27483. * @param {Highcharts.LegendOptions} options
  27484. * Legend options.
  27485. */
  27486. init(chart, options) {
  27487. /**
  27488. * Chart of this legend.
  27489. *
  27490. * @readonly
  27491. * @name Highcharts.Legend#chart
  27492. * @type {Highcharts.Chart}
  27493. */
  27494. this.chart = chart;
  27495. this.setOptions(options);
  27496. if (options.enabled) {
  27497. // Render it
  27498. this.render();
  27499. // move checkboxes
  27500. addEvent(this.chart, 'endResize', function () {
  27501. this.legend.positionCheckboxes();
  27502. });
  27503. // On Legend.init and Legend.update, make sure that proximate layout
  27504. // events are either added or removed (#18362).
  27505. addEvent(this.chart, 'render', () => {
  27506. if (this.proximate) {
  27507. this.proximatePositions();
  27508. this.positionItems();
  27509. }
  27510. });
  27511. }
  27512. }
  27513. /**
  27514. * @private
  27515. * @function Highcharts.Legend#setOptions
  27516. * @param {Highcharts.LegendOptions} options
  27517. */
  27518. setOptions(options) {
  27519. const padding = pick(options.padding, 8);
  27520. /**
  27521. * Legend options.
  27522. *
  27523. * @readonly
  27524. * @name Highcharts.Legend#options
  27525. * @type {Highcharts.LegendOptions}
  27526. */
  27527. this.options = options;
  27528. if (!this.chart.styledMode) {
  27529. this.itemStyle = options.itemStyle;
  27530. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  27531. }
  27532. this.itemMarginTop = options.itemMarginTop;
  27533. this.itemMarginBottom = options.itemMarginBottom;
  27534. this.padding = padding;
  27535. this.initialItemY = padding - 5; // 5 is pixels above the text
  27536. this.symbolWidth = pick(options.symbolWidth, 16);
  27537. this.pages = [];
  27538. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  27539. // #12705: baseline has to be reset on every update
  27540. this.baseline = void 0;
  27541. }
  27542. /**
  27543. * Update the legend with new options. Equivalent to running `chart.update`
  27544. * with a legend configuration option.
  27545. *
  27546. * @sample highcharts/legend/legend-update/
  27547. * Legend update
  27548. *
  27549. * @function Highcharts.Legend#update
  27550. *
  27551. * @param {Highcharts.LegendOptions} options
  27552. * Legend options.
  27553. *
  27554. * @param {boolean} [redraw=true]
  27555. * Whether to redraw the chart after the axis is altered. If doing more
  27556. * operations on the chart, it is a good idea to set redraw to false and
  27557. * call {@link Chart#redraw} after. Whether to redraw the chart.
  27558. *
  27559. * @emits Highcharts.Legends#event:afterUpdate
  27560. */
  27561. update(options, redraw) {
  27562. const chart = this.chart;
  27563. this.setOptions(merge(true, this.options, options));
  27564. this.destroy();
  27565. chart.isDirtyLegend = chart.isDirtyBox = true;
  27566. if (pick(redraw, true)) {
  27567. chart.redraw();
  27568. }
  27569. fireEvent(this, 'afterUpdate');
  27570. }
  27571. /**
  27572. * Set the colors for the legend item.
  27573. *
  27574. * @private
  27575. * @function Highcharts.Legend#colorizeItem
  27576. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27577. * A Series or Point instance
  27578. * @param {boolean} [visible=false]
  27579. * Dimmed or colored
  27580. *
  27581. * @todo
  27582. * Make events official: Fires the event `afterColorizeItem`.
  27583. */
  27584. colorizeItem(item, visible) {
  27585. const { group, label, line, symbol } = item.legendItem || {};
  27586. if (group) {
  27587. group[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  27588. }
  27589. if (!this.chart.styledMode) {
  27590. const { itemHiddenStyle } = this, hiddenColor = itemHiddenStyle.color, symbolColor = visible ?
  27591. (item.color || hiddenColor) :
  27592. hiddenColor, markerOptions = item.options && item.options.marker;
  27593. let symbolAttr = { fill: symbolColor };
  27594. label === null || label === void 0 ? void 0 : label.css(merge(visible ? this.itemStyle : itemHiddenStyle));
  27595. line === null || line === void 0 ? void 0 : line.attr({ stroke: symbolColor });
  27596. if (symbol) {
  27597. // Apply marker options
  27598. if (markerOptions && symbol.isMarker) { // #585
  27599. symbolAttr = item.pointAttribs();
  27600. if (!visible) {
  27601. // #6769
  27602. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  27603. }
  27604. }
  27605. symbol.attr(symbolAttr);
  27606. }
  27607. }
  27608. fireEvent(this, 'afterColorizeItem', { item, visible });
  27609. }
  27610. /**
  27611. * @private
  27612. * @function Highcharts.Legend#positionItems
  27613. */
  27614. positionItems() {
  27615. // Now that the legend width and height are established, put the items
  27616. // in the final position
  27617. this.allItems.forEach(this.positionItem, this);
  27618. if (!this.chart.isResizing) {
  27619. this.positionCheckboxes();
  27620. }
  27621. }
  27622. /**
  27623. * Position the legend item.
  27624. *
  27625. * @private
  27626. * @function Highcharts.Legend#positionItem
  27627. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27628. * The item to position
  27629. */
  27630. positionItem(item) {
  27631. const legend = this, { group, x = 0, y = 0 } = item.legendItem || {}, options = legend.options, symbolPadding = options.symbolPadding, ltr = !options.rtl, checkbox = item.checkbox;
  27632. if (group && group.element) {
  27633. const attribs = {
  27634. translateX: ltr ?
  27635. x :
  27636. legend.legendWidth - x - 2 * symbolPadding - 4,
  27637. translateY: y
  27638. };
  27639. const complete = () => {
  27640. fireEvent(this, 'afterPositionItem', { item });
  27641. };
  27642. group[defined(group.translateY) ? 'animate' : 'attr'](attribs, void 0, complete);
  27643. }
  27644. if (checkbox) {
  27645. checkbox.x = x;
  27646. checkbox.y = y;
  27647. }
  27648. }
  27649. /**
  27650. * Destroy a single legend item, used internally on removing series items.
  27651. *
  27652. * @private
  27653. * @function Highcharts.Legend#destroyItem
  27654. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27655. * The item to remove
  27656. */
  27657. destroyItem(item) {
  27658. const checkbox = item.checkbox, legendItem = item.legendItem || {};
  27659. // destroy SVG elements
  27660. for (const key of ['group', 'label', 'line', 'symbol']) {
  27661. if (legendItem[key]) {
  27662. legendItem[key] = legendItem[key].destroy();
  27663. }
  27664. }
  27665. if (checkbox) {
  27666. discardElement(checkbox);
  27667. }
  27668. item.legendItem = void 0;
  27669. }
  27670. /**
  27671. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  27672. * must be called after destruction.
  27673. *
  27674. * @private
  27675. * @function Highcharts.Legend#destroy
  27676. */
  27677. destroy() {
  27678. const legend = this;
  27679. // Destroy items
  27680. for (const item of this.getAllItems()) {
  27681. this.destroyItem(item);
  27682. }
  27683. // Destroy legend elements
  27684. for (const key of [
  27685. 'clipRect',
  27686. 'up',
  27687. 'down',
  27688. 'pager',
  27689. 'nav',
  27690. 'box',
  27691. 'title',
  27692. 'group'
  27693. ]) {
  27694. if (legend[key]) {
  27695. legend[key] = legend[key].destroy();
  27696. }
  27697. }
  27698. this.display = null; // Reset in .render on update.
  27699. }
  27700. /**
  27701. * Position the checkboxes after the width is determined.
  27702. *
  27703. * @private
  27704. * @function Highcharts.Legend#positionCheckboxes
  27705. */
  27706. positionCheckboxes() {
  27707. const alignAttr = this.group && this.group.alignAttr, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
  27708. let translateY;
  27709. if (alignAttr) {
  27710. translateY = alignAttr.translateY;
  27711. this.allItems.forEach(function (item) {
  27712. const checkbox = item.checkbox;
  27713. let top;
  27714. if (checkbox) {
  27715. top = translateY + titleHeight + checkbox.y +
  27716. (this.scrollOffset || 0) + 3;
  27717. css(checkbox, {
  27718. left: (alignAttr.translateX + item.checkboxOffset +
  27719. checkbox.x - 20) + 'px',
  27720. top: top + 'px',
  27721. display: this.proximate || (top > translateY - 6 &&
  27722. top < translateY + clipHeight - 6) ?
  27723. '' :
  27724. 'none'
  27725. });
  27726. }
  27727. }, this);
  27728. }
  27729. }
  27730. /**
  27731. * Render the legend title on top of the legend.
  27732. *
  27733. * @private
  27734. * @function Highcharts.Legend#renderTitle
  27735. */
  27736. renderTitle() {
  27737. const options = this.options, padding = this.padding, titleOptions = options.title;
  27738. let bBox, titleHeight = 0;
  27739. if (titleOptions.text) {
  27740. if (!this.title) {
  27741. /**
  27742. * SVG element of the legend title.
  27743. *
  27744. * @readonly
  27745. * @name Highcharts.Legend#title
  27746. * @type {Highcharts.SVGElement}
  27747. */
  27748. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, void 0, void 0, void 0, options.useHTML, void 0, 'legend-title')
  27749. .attr({ zIndex: 1 });
  27750. if (!this.chart.styledMode) {
  27751. this.title.css(titleOptions.style);
  27752. }
  27753. this.title.add(this.group);
  27754. }
  27755. // Set the max title width (#7253)
  27756. if (!titleOptions.width) {
  27757. this.title.css({
  27758. width: this.maxLegendWidth + 'px'
  27759. });
  27760. }
  27761. bBox = this.title.getBBox();
  27762. titleHeight = bBox.height;
  27763. this.offsetWidth = bBox.width; // #1717
  27764. this.contentGroup.attr({ translateY: titleHeight });
  27765. }
  27766. this.titleHeight = titleHeight;
  27767. }
  27768. /**
  27769. * Set the legend item text.
  27770. *
  27771. * @function Highcharts.Legend#setText
  27772. * @param {Highcharts.Point|Highcharts.Series} item
  27773. * The item for which to update the text in the legend.
  27774. */
  27775. setText(item) {
  27776. const options = this.options;
  27777. item.legendItem.label.attr({
  27778. text: options.labelFormat ?
  27779. format(options.labelFormat, item, this.chart) :
  27780. options.labelFormatter.call(item)
  27781. });
  27782. }
  27783. /**
  27784. * Render a single specific legend item. Called internally from the `render`
  27785. * function.
  27786. *
  27787. * @private
  27788. * @function Highcharts.Legend#renderItem
  27789. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27790. * The item to render.
  27791. */
  27792. renderItem(item) {
  27793. const legend = this, legendItem = item.legendItem = item.legendItem || {}, chart = legend.chart, renderer = chart.renderer, options = legend.options, horizontal = options.layout === 'horizontal', symbolWidth = legend.symbolWidth, symbolPadding = options.symbolPadding || 0, itemStyle = legend.itemStyle, itemHiddenStyle = legend.itemHiddenStyle, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, ltr = !options.rtl, isSeries = !item.series, series = !isSeries && item.series.drawLegendSymbol ?
  27794. item.series :
  27795. item, seriesOptions = series.options, showCheckbox = (legend.createCheckboxForItem) &&
  27796. seriesOptions &&
  27797. seriesOptions.showCheckbox, useHTML = options.useHTML, itemClassName = item.options.className;
  27798. let label = legendItem.label,
  27799. // full width minus text width
  27800. itemExtraWidth = symbolWidth + symbolPadding +
  27801. itemDistance + (showCheckbox ? 20 : 0);
  27802. if (!label) { // generate it once, later move it
  27803. // Generate the group box, a group to hold the symbol and text. Text
  27804. // is to be appended in Legend class.
  27805. legendItem.group = renderer
  27806. .g('legend-item')
  27807. .addClass('highcharts-' + series.type + '-series ' +
  27808. 'highcharts-color-' + item.colorIndex +
  27809. (itemClassName ? ' ' + itemClassName : '') +
  27810. (isSeries ?
  27811. ' highcharts-series-' + item.index :
  27812. ''))
  27813. .attr({ zIndex: 1 })
  27814. .add(legend.scrollGroup);
  27815. // Generate the list item text and add it to the group
  27816. legendItem.label = label = renderer.text('', ltr ?
  27817. symbolWidth + symbolPadding :
  27818. -symbolPadding, legend.baseline || 0, useHTML);
  27819. if (!chart.styledMode) {
  27820. // merge to prevent modifying original (#1021)
  27821. label.css(merge(item.visible ?
  27822. itemStyle :
  27823. itemHiddenStyle));
  27824. }
  27825. label
  27826. .attr({
  27827. align: ltr ? 'left' : 'right',
  27828. zIndex: 2
  27829. })
  27830. .add(legendItem.group);
  27831. // Get the baseline for the first item - the font size is equal for
  27832. // all
  27833. if (!legend.baseline) {
  27834. legend.fontMetrics = renderer.fontMetrics(label);
  27835. legend.baseline =
  27836. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  27837. label.attr('y', legend.baseline);
  27838. legend.symbolHeight =
  27839. pick(options.symbolHeight, legend.fontMetrics.f);
  27840. if (options.squareSymbol) {
  27841. legend.symbolWidth = pick(options.symbolWidth, Math.max(legend.symbolHeight, 16));
  27842. itemExtraWidth = legend.symbolWidth + symbolPadding +
  27843. itemDistance + (showCheckbox ? 20 : 0);
  27844. if (ltr) {
  27845. label.attr('x', legend.symbolWidth + symbolPadding);
  27846. }
  27847. }
  27848. }
  27849. // Draw the legend symbol inside the group box
  27850. series.drawLegendSymbol(legend, item);
  27851. if (legend.setItemEvents) {
  27852. legend.setItemEvents(item, label, useHTML);
  27853. }
  27854. }
  27855. // Add the HTML checkbox on top
  27856. if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
  27857. legend.createCheckboxForItem(item);
  27858. }
  27859. // Colorize the items
  27860. legend.colorizeItem(item, item.visible);
  27861. // Take care of max width and text overflow (#6659)
  27862. if (chart.styledMode || !itemStyle.width) {
  27863. label.css({
  27864. width: ((options.itemWidth ||
  27865. legend.widthOption ||
  27866. chart.spacingBox.width) - itemExtraWidth) + 'px'
  27867. });
  27868. }
  27869. // Always update the text
  27870. legend.setText(item);
  27871. // calculate the positions for the next line
  27872. const bBox = label.getBBox();
  27873. const fontMetricsH = (legend.fontMetrics && legend.fontMetrics.h) || 0;
  27874. item.itemWidth = item.checkboxOffset =
  27875. options.itemWidth ||
  27876. legendItem.labelWidth ||
  27877. bBox.width + itemExtraWidth;
  27878. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  27879. legend.totalItemWidth += item.itemWidth;
  27880. legend.itemHeight = item.itemHeight = Math.round(legendItem.labelHeight ||
  27881. // use bBox for multiline (#16398)
  27882. (bBox.height > fontMetricsH * 1.5 ? bBox.height : fontMetricsH));
  27883. }
  27884. /**
  27885. * Get the position of the item in the layout. We now know the
  27886. * maxItemWidth from the previous loop.
  27887. *
  27888. * @private
  27889. * @function Highcharts.Legend#layoutItem
  27890. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27891. */
  27892. layoutItem(item) {
  27893. const options = this.options, padding = this.padding, horizontal = options.layout === 'horizontal', itemHeight = item.itemHeight, itemMarginBottom = this.itemMarginBottom, itemMarginTop = this.itemMarginTop, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, maxLegendWidth = this.maxLegendWidth, itemWidth = (options.alignColumns &&
  27894. this.totalItemWidth > maxLegendWidth) ?
  27895. this.maxItemWidth :
  27896. item.itemWidth, legendItem = item.legendItem || {};
  27897. // If the item exceeds the width, start a new line
  27898. if (horizontal &&
  27899. this.itemX - padding + itemWidth > maxLegendWidth) {
  27900. this.itemX = padding;
  27901. if (this.lastLineHeight) { // Not for the first line (#10167)
  27902. this.itemY += (itemMarginTop +
  27903. this.lastLineHeight +
  27904. itemMarginBottom);
  27905. }
  27906. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  27907. }
  27908. // Set the edge positions
  27909. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  27910. this.lastLineHeight = Math.max(// #915
  27911. itemHeight, this.lastLineHeight);
  27912. // cache the position of the newly generated or reordered items
  27913. legendItem.x = this.itemX;
  27914. legendItem.y = this.itemY;
  27915. // advance
  27916. if (horizontal) {
  27917. this.itemX += itemWidth;
  27918. }
  27919. else {
  27920. this.itemY +=
  27921. itemMarginTop + itemHeight + itemMarginBottom;
  27922. this.lastLineHeight = itemHeight;
  27923. }
  27924. // the width of the widest item
  27925. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  27926. // decrease by itemDistance only when no checkbox #4853
  27927. 0 :
  27928. itemDistance) : itemWidth) + padding, this.offsetWidth);
  27929. }
  27930. /**
  27931. * Get all items, which is one item per series for most series and one
  27932. * item per point for pie series and its derivatives. Fires the event
  27933. * `afterGetAllItems`.
  27934. *
  27935. * @private
  27936. * @function Highcharts.Legend#getAllItems
  27937. * @return {Array<(Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series)>}
  27938. * The current items in the legend.
  27939. * @emits Highcharts.Legend#event:afterGetAllItems
  27940. */
  27941. getAllItems() {
  27942. let allItems = [];
  27943. this.chart.series.forEach(function (series) {
  27944. const seriesOptions = series && series.options;
  27945. // Handle showInLegend. If the series is linked to another series,
  27946. // defaults to false.
  27947. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  27948. // Use points or series for the legend item depending on
  27949. // legendType
  27950. allItems = allItems.concat((series.legendItem || {}).labels ||
  27951. (seriesOptions.legendType === 'point' ?
  27952. series.data :
  27953. series));
  27954. }
  27955. });
  27956. fireEvent(this, 'afterGetAllItems', { allItems });
  27957. return allItems;
  27958. }
  27959. /**
  27960. * Get a short, three letter string reflecting the alignment and layout.
  27961. *
  27962. * @private
  27963. * @function Highcharts.Legend#getAlignment
  27964. * @return {string}
  27965. * The alignment, empty string if floating
  27966. */
  27967. getAlignment() {
  27968. const options = this.options;
  27969. // Use the first letter of each alignment option in order to detect
  27970. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  27971. if (this.proximate) {
  27972. return options.align.charAt(0) + 'tv';
  27973. }
  27974. return options.floating ? '' : (options.align.charAt(0) +
  27975. options.verticalAlign.charAt(0) +
  27976. options.layout.charAt(0));
  27977. }
  27978. /**
  27979. * Adjust the chart margins by reserving space for the legend on only one
  27980. * side of the chart. If the position is set to a corner, top or bottom is
  27981. * reserved for horizontal legends and left or right for vertical ones.
  27982. *
  27983. * @private
  27984. * @function Highcharts.Legend#adjustMargins
  27985. * @param {Array<number>} margin
  27986. * @param {Array<number>} spacing
  27987. */
  27988. adjustMargins(margin, spacing) {
  27989. const chart = this.chart, options = this.options, alignment = this.getAlignment();
  27990. if (alignment) {
  27991. ([
  27992. /(lth|ct|rth)/,
  27993. /(rtv|rm|rbv)/,
  27994. /(rbh|cb|lbh)/,
  27995. /(lbv|lm|ltv)/
  27996. ]).forEach(function (alignments, side) {
  27997. if (alignments.test(alignment) && !defined(margin[side])) {
  27998. // Now we have detected on which side of the chart we should
  27999. // reserve space for the legend
  28000. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  28001. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  28002. pick(options.margin, 12) +
  28003. spacing[side] +
  28004. (chart.titleOffset[side] || 0)));
  28005. }
  28006. });
  28007. }
  28008. }
  28009. /**
  28010. * @private
  28011. * @function Highcharts.Legend#proximatePositions
  28012. */
  28013. proximatePositions() {
  28014. const chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
  28015. this.allItems.forEach(function (item) {
  28016. let lastPoint, height, useFirstPoint = alignLeft, target, top;
  28017. if (item.yAxis) {
  28018. if (item.xAxis.options.reversed) {
  28019. useFirstPoint = !useFirstPoint;
  28020. }
  28021. if (item.points) {
  28022. lastPoint = find(useFirstPoint ?
  28023. item.points :
  28024. item.points.slice(0).reverse(), function (item) {
  28025. return isNumber(item.plotY);
  28026. });
  28027. }
  28028. height = this.itemMarginTop +
  28029. item.legendItem.label.getBBox().height +
  28030. this.itemMarginBottom;
  28031. top = item.yAxis.top - chart.plotTop;
  28032. if (item.visible) {
  28033. target = lastPoint ?
  28034. lastPoint.plotY :
  28035. item.yAxis.height;
  28036. target += top - 0.3 * height;
  28037. }
  28038. else {
  28039. target = top + item.yAxis.height;
  28040. }
  28041. boxes.push({
  28042. target: target,
  28043. size: height,
  28044. item
  28045. });
  28046. }
  28047. }, this);
  28048. let legendItem;
  28049. for (const box of distribute(boxes, chart.plotHeight)) {
  28050. legendItem = box.item.legendItem || {};
  28051. if (isNumber(box.pos)) {
  28052. legendItem.y = chart.plotTop - chart.spacing[0] + box.pos;
  28053. }
  28054. }
  28055. }
  28056. /**
  28057. * Render the legend. This method can be called both before and after
  28058. * `chart.render`. If called after, it will only rearrange items instead
  28059. * of creating new ones. Called internally on initial render and after
  28060. * redraws.
  28061. *
  28062. * @private
  28063. * @function Highcharts.Legend#render
  28064. */
  28065. render() {
  28066. const legend = this, chart = legend.chart, renderer = chart.renderer, options = legend.options, padding = legend.padding,
  28067. // add each series or point
  28068. allItems = legend.getAllItems();
  28069. let display, legendWidth, legendHeight, legendGroup = legend.group, allowedWidth, box = legend.box;
  28070. legend.itemX = padding;
  28071. legend.itemY = legend.initialItemY;
  28072. legend.offsetWidth = 0;
  28073. legend.lastItemY = 0;
  28074. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  28075. // Compute how wide the legend is allowed to be
  28076. allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
  28077. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  28078. allowedWidth /= 2;
  28079. }
  28080. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  28081. if (!legendGroup) {
  28082. /**
  28083. * SVG group of the legend.
  28084. *
  28085. * @readonly
  28086. * @name Highcharts.Legend#group
  28087. * @type {Highcharts.SVGElement}
  28088. */
  28089. legend.group = legendGroup = renderer
  28090. .g('legend')
  28091. .addClass(options.className || '')
  28092. .attr({ zIndex: 7 })
  28093. .add();
  28094. legend.contentGroup = renderer
  28095. .g()
  28096. .attr({ zIndex: 1 }) // above background
  28097. .add(legendGroup);
  28098. legend.scrollGroup = renderer
  28099. .g()
  28100. .add(legend.contentGroup);
  28101. }
  28102. legend.renderTitle();
  28103. // sort by legendIndex
  28104. stableSort(allItems, (a, b) => ((a.options && a.options.legendIndex) || 0) -
  28105. ((b.options && b.options.legendIndex) || 0));
  28106. // reversed legend
  28107. if (options.reversed) {
  28108. allItems.reverse();
  28109. }
  28110. /**
  28111. * All items for the legend, which is an array of series for most series
  28112. * and an array of points for pie series and its derivatives.
  28113. *
  28114. * @readonly
  28115. * @name Highcharts.Legend#allItems
  28116. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  28117. */
  28118. legend.allItems = allItems;
  28119. legend.display = display = !!allItems.length;
  28120. // Render the items. First we run a loop to set the text and properties
  28121. // and read all the bounding boxes. The next loop computes the item
  28122. // positions based on the bounding boxes.
  28123. legend.lastLineHeight = 0;
  28124. legend.maxItemWidth = 0;
  28125. legend.totalItemWidth = 0;
  28126. legend.itemHeight = 0;
  28127. allItems.forEach(legend.renderItem, legend);
  28128. allItems.forEach(legend.layoutItem, legend);
  28129. // Get the box
  28130. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  28131. legendHeight = legend.lastItemY + legend.lastLineHeight +
  28132. legend.titleHeight;
  28133. legendHeight = legend.handleOverflow(legendHeight);
  28134. legendHeight += padding;
  28135. // Draw the border and/or background
  28136. if (!box) {
  28137. /**
  28138. * SVG element of the legend box.
  28139. *
  28140. * @readonly
  28141. * @name Highcharts.Legend#box
  28142. * @type {Highcharts.SVGElement}
  28143. */
  28144. legend.box = box = renderer.rect()
  28145. .addClass('highcharts-legend-box')
  28146. .attr({
  28147. r: options.borderRadius
  28148. })
  28149. .add(legendGroup);
  28150. }
  28151. // Presentational
  28152. if (!chart.styledMode) {
  28153. box
  28154. .attr({
  28155. stroke: options.borderColor,
  28156. 'stroke-width': options.borderWidth || 0,
  28157. fill: options.backgroundColor || 'none'
  28158. })
  28159. .shadow(options.shadow);
  28160. }
  28161. if (legendWidth > 0 && legendHeight > 0) {
  28162. box[box.placed ? 'animate' : 'attr'](box.crisp.call({}, {
  28163. x: 0,
  28164. y: 0,
  28165. width: legendWidth,
  28166. height: legendHeight
  28167. }, box.strokeWidth()));
  28168. }
  28169. // hide the border if no items
  28170. legendGroup[display ? 'show' : 'hide']();
  28171. // Open for responsiveness
  28172. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  28173. legendWidth = legendHeight = 0;
  28174. }
  28175. legend.legendWidth = legendWidth;
  28176. legend.legendHeight = legendHeight;
  28177. if (display) {
  28178. legend.align();
  28179. }
  28180. if (!this.proximate) {
  28181. this.positionItems();
  28182. }
  28183. fireEvent(this, 'afterRender');
  28184. }
  28185. /**
  28186. * Align the legend to chart's box.
  28187. *
  28188. * @private
  28189. * @function Highcharts.align
  28190. * @param {Highcharts.BBoxObject} alignTo
  28191. */
  28192. align(alignTo = this.chart.spacingBox) {
  28193. const chart = this.chart, options = this.options;
  28194. // If aligning to the top and the layout is horizontal, adjust for
  28195. // the title (#7428)
  28196. let y = alignTo.y;
  28197. if (/(lth|ct|rth)/.test(this.getAlignment()) &&
  28198. chart.titleOffset[0] > 0) {
  28199. y += chart.titleOffset[0];
  28200. }
  28201. else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
  28202. chart.titleOffset[2] > 0) {
  28203. y -= chart.titleOffset[2];
  28204. }
  28205. if (y !== alignTo.y) {
  28206. alignTo = merge(alignTo, { y });
  28207. }
  28208. if (!chart.hasRendered) {
  28209. // Avoid animation when adjusting alignment for responsiveness and
  28210. // colorAxis label layout
  28211. this.group.placed = false;
  28212. }
  28213. this.group.align(merge(options, {
  28214. width: this.legendWidth,
  28215. height: this.legendHeight,
  28216. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  28217. }), true, alignTo);
  28218. }
  28219. /**
  28220. * Set up the overflow handling by adding navigation with up and down arrows
  28221. * below the legend.
  28222. *
  28223. * @private
  28224. * @function Highcharts.Legend#handleOverflow
  28225. */
  28226. handleOverflow(legendHeight) {
  28227. const legend = this, chart = this.chart, renderer = chart.renderer, options = this.options, optionsY = options.y, alignTop = options.verticalAlign === 'top', padding = this.padding, maxHeight = options.maxHeight, navOptions = options.navigation, animation = pick(navOptions.animation, true), arrowSize = navOptions.arrowSize || 12, pages = this.pages, allItems = this.allItems, clipToHeight = function (height) {
  28228. if (typeof height === 'number') {
  28229. clipRect.attr({
  28230. height: height
  28231. });
  28232. }
  28233. else if (clipRect) { // Reset (#5912)
  28234. legend.clipRect = clipRect.destroy();
  28235. legend.contentGroup.clip();
  28236. }
  28237. // useHTML
  28238. if (legend.contentGroup.div) {
  28239. legend.contentGroup.div.style.clip = height ?
  28240. 'rect(' + padding + 'px,9999px,' +
  28241. (padding + height) + 'px,0)' :
  28242. 'auto';
  28243. }
  28244. }, addTracker = function (key) {
  28245. legend[key] = renderer
  28246. .circle(0, 0, arrowSize * 1.3)
  28247. .translate(arrowSize / 2, arrowSize / 2)
  28248. .add(nav);
  28249. if (!chart.styledMode) {
  28250. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  28251. }
  28252. return legend[key];
  28253. };
  28254. let clipHeight, lastY, legendItem, spaceHeight = (chart.spacingBox.height +
  28255. (alignTop ? -optionsY : optionsY) - padding), nav = this.nav, clipRect = this.clipRect;
  28256. // Adjust the height
  28257. if (options.layout === 'horizontal' &&
  28258. options.verticalAlign !== 'middle' &&
  28259. !options.floating) {
  28260. spaceHeight /= 2;
  28261. }
  28262. if (maxHeight) {
  28263. spaceHeight = Math.min(spaceHeight, maxHeight);
  28264. }
  28265. // Reset the legend height and adjust the clipping rectangle
  28266. pages.length = 0;
  28267. if (legendHeight &&
  28268. spaceHeight > 0 &&
  28269. legendHeight > spaceHeight &&
  28270. navOptions.enabled !== false) {
  28271. this.clipHeight = clipHeight =
  28272. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  28273. this.currentPage = pick(this.currentPage, 1);
  28274. this.fullHeight = legendHeight;
  28275. // Fill pages with Y positions so that the top of each a legend item
  28276. // defines the scroll top for each page (#2098)
  28277. allItems.forEach((item, i) => {
  28278. legendItem = item.legendItem || {};
  28279. const y = legendItem.y || 0, h = Math.round(legendItem.label.getBBox().height);
  28280. let len = pages.length;
  28281. if (!len || (y - pages[len - 1] > clipHeight &&
  28282. (lastY || y) !== pages[len - 1])) {
  28283. pages.push(lastY || y);
  28284. len++;
  28285. }
  28286. // Keep track of which page each item is on
  28287. legendItem.pageIx = len - 1;
  28288. if (lastY) {
  28289. (allItems[i - 1].legendItem || {}).pageIx = len - 1;
  28290. }
  28291. // add the last page if needed (#2617, #13683)
  28292. if (
  28293. // check the last item
  28294. i === allItems.length - 1 &&
  28295. // if adding next page is needed (#18768)
  28296. y + h - pages[len - 1] > clipHeight &&
  28297. y > pages[len - 1]) {
  28298. pages.push(y);
  28299. legendItem.pageIx = len;
  28300. }
  28301. if (y !== lastY) {
  28302. lastY = y;
  28303. }
  28304. });
  28305. // Only apply clipping if needed. Clipping causes blurred legend in
  28306. // PDF export (#1787)
  28307. if (!clipRect) {
  28308. clipRect = legend.clipRect =
  28309. renderer.clipRect(0, padding - 2, 9999, 0);
  28310. legend.contentGroup.clip(clipRect);
  28311. }
  28312. clipToHeight(clipHeight);
  28313. // Add navigation elements
  28314. if (!nav) {
  28315. this.nav = nav = renderer.g()
  28316. .attr({ zIndex: 1 })
  28317. .add(this.group);
  28318. this.up = renderer
  28319. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  28320. .add(nav);
  28321. addTracker('upTracker')
  28322. .on('click', function () {
  28323. legend.scroll(-1, animation);
  28324. });
  28325. this.pager = renderer.text('', 15, 10)
  28326. .addClass('highcharts-legend-navigation');
  28327. if (!chart.styledMode && navOptions.style) {
  28328. this.pager.css(navOptions.style);
  28329. }
  28330. this.pager.add(nav);
  28331. this.down = renderer
  28332. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  28333. .add(nav);
  28334. addTracker('downTracker')
  28335. .on('click', function () {
  28336. legend.scroll(1, animation);
  28337. });
  28338. }
  28339. // Set initial position
  28340. legend.scroll(0);
  28341. legendHeight = spaceHeight;
  28342. // Reset
  28343. }
  28344. else if (nav) {
  28345. clipToHeight();
  28346. this.nav = nav.destroy(); // #6322
  28347. this.scrollGroup.attr({
  28348. translateY: 1
  28349. });
  28350. this.clipHeight = 0; // #1379
  28351. }
  28352. return legendHeight;
  28353. }
  28354. /**
  28355. * Scroll the legend by a number of pages.
  28356. *
  28357. * @private
  28358. * @function Highcharts.Legend#scroll
  28359. *
  28360. * @param {number} scrollBy
  28361. * The number of pages to scroll.
  28362. *
  28363. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  28364. * Whether and how to apply animation.
  28365. *
  28366. */
  28367. scroll(scrollBy, animation) {
  28368. const chart = this.chart, pages = this.pages, pageCount = pages.length, clipHeight = this.clipHeight, navOptions = this.options.navigation, pager = this.pager, padding = this.padding;
  28369. let currentPage = this.currentPage + scrollBy;
  28370. // When resizing while looking at the last page
  28371. if (currentPage > pageCount) {
  28372. currentPage = pageCount;
  28373. }
  28374. if (currentPage > 0) {
  28375. if (typeof animation !== 'undefined') {
  28376. setAnimation(animation, chart);
  28377. }
  28378. this.nav.attr({
  28379. translateX: padding,
  28380. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  28381. visibility: 'inherit'
  28382. });
  28383. [this.up, this.upTracker].forEach(function (elem) {
  28384. elem.attr({
  28385. 'class': currentPage === 1 ?
  28386. 'highcharts-legend-nav-inactive' :
  28387. 'highcharts-legend-nav-active'
  28388. });
  28389. });
  28390. pager.attr({
  28391. text: currentPage + '/' + pageCount
  28392. });
  28393. [this.down, this.downTracker].forEach(function (elem) {
  28394. elem.attr({
  28395. // adjust to text width
  28396. x: 18 + this.pager.getBBox().width,
  28397. 'class': currentPage === pageCount ?
  28398. 'highcharts-legend-nav-inactive' :
  28399. 'highcharts-legend-nav-active'
  28400. });
  28401. }, this);
  28402. if (!chart.styledMode) {
  28403. this.up
  28404. .attr({
  28405. fill: currentPage === 1 ?
  28406. navOptions.inactiveColor :
  28407. navOptions.activeColor
  28408. });
  28409. this.upTracker
  28410. .css({
  28411. cursor: currentPage === 1 ? 'default' : 'pointer'
  28412. });
  28413. this.down
  28414. .attr({
  28415. fill: currentPage === pageCount ?
  28416. navOptions.inactiveColor :
  28417. navOptions.activeColor
  28418. });
  28419. this.downTracker
  28420. .css({
  28421. cursor: currentPage === pageCount ?
  28422. 'default' :
  28423. 'pointer'
  28424. });
  28425. }
  28426. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  28427. this.scrollGroup.animate({
  28428. translateY: this.scrollOffset
  28429. });
  28430. this.currentPage = currentPage;
  28431. this.positionCheckboxes();
  28432. // Fire event after scroll animation is complete
  28433. const animOptions = animObject(pick(animation, chart.renderer.globalAnimation, true));
  28434. syncTimeout(() => {
  28435. fireEvent(this, 'afterScroll', { currentPage });
  28436. }, animOptions.duration);
  28437. }
  28438. }
  28439. /**
  28440. * @private
  28441. * @function Highcharts.Legend#setItemEvents
  28442. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28443. * @param {Highcharts.SVGElement} legendLabel
  28444. * @param {boolean} [useHTML=false]
  28445. * @emits Highcharts.Point#event:legendItemClick
  28446. * @emits Highcharts.Series#event:legendItemClick
  28447. */
  28448. setItemEvents(item, legendLabel, useHTML) {
  28449. const legend = this, legendItem = item.legendItem || {}, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, activeClass = 'highcharts-legend-' +
  28450. (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode,
  28451. // When `useHTML`, the symbol is rendered in other group, so
  28452. // we need to apply events listeners to both places
  28453. legendElements = useHTML ?
  28454. [legendLabel, legendItem.symbol] :
  28455. [legendItem.group];
  28456. const setOtherItemsState = (state) => {
  28457. legend.allItems.forEach((otherItem) => {
  28458. if (item !== otherItem) {
  28459. [otherItem]
  28460. .concat(otherItem.linkedSeries || [])
  28461. .forEach((otherItem) => {
  28462. otherItem.setState(state, !isPoint);
  28463. });
  28464. }
  28465. });
  28466. };
  28467. // Set the events on the item group, or in case of useHTML, the item
  28468. // itself (#1249)
  28469. for (const element of legendElements) {
  28470. if (element) {
  28471. element
  28472. .on('mouseover', function () {
  28473. if (item.visible) {
  28474. setOtherItemsState('inactive');
  28475. }
  28476. item.setState('hover');
  28477. // A CSS class to dim or hide other than the hovered
  28478. // series.
  28479. // Works only if hovered series is visible (#10071).
  28480. if (item.visible) {
  28481. boxWrapper.addClass(activeClass);
  28482. }
  28483. if (!styledMode) {
  28484. legendLabel.css(legend.options.itemHoverStyle);
  28485. }
  28486. })
  28487. .on('mouseout', function () {
  28488. if (!legend.chart.styledMode) {
  28489. legendLabel.css(merge(item.visible ?
  28490. legend.itemStyle :
  28491. legend.itemHiddenStyle));
  28492. }
  28493. setOtherItemsState('');
  28494. // A CSS class to dim or hide other than the hovered
  28495. // series.
  28496. boxWrapper.removeClass(activeClass);
  28497. item.setState();
  28498. })
  28499. .on('click', function (event) {
  28500. const strLegendItemClick = 'legendItemClick', fnLegendItemClick = function () {
  28501. if (item.setVisible) {
  28502. item.setVisible();
  28503. }
  28504. // Reset inactive state
  28505. setOtherItemsState(item.visible ? 'inactive' : '');
  28506. };
  28507. // A CSS class to dim or hide other than the hovered
  28508. // series. Event handling in iOS causes the activeClass
  28509. // to be added prior to click in some cases (#7418).
  28510. boxWrapper.removeClass(activeClass);
  28511. // Pass over the click/touch event. #4.
  28512. event = {
  28513. browserEvent: event
  28514. };
  28515. // click the name or symbol
  28516. if (item.firePointEvent) { // point
  28517. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  28518. }
  28519. else {
  28520. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  28521. }
  28522. });
  28523. }
  28524. }
  28525. }
  28526. /**
  28527. * @private
  28528. * @function Highcharts.Legend#createCheckboxForItem
  28529. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28530. * @emits Highcharts.Series#event:checkboxClick
  28531. */
  28532. createCheckboxForItem(item) {
  28533. const legend = this;
  28534. item.checkbox = createElement('input', {
  28535. type: 'checkbox',
  28536. className: 'highcharts-legend-checkbox',
  28537. checked: item.selected,
  28538. defaultChecked: item.selected // required by IE7
  28539. }, legend.options.itemCheckboxStyle, legend.chart.container);
  28540. addEvent(item.checkbox, 'click', function (event) {
  28541. const target = event.target;
  28542. fireEvent(item.series || item, 'checkboxClick', {
  28543. checked: target.checked,
  28544. item: item
  28545. }, function () {
  28546. item.select();
  28547. });
  28548. });
  28549. }
  28550. }
  28551. /* *
  28552. *
  28553. * Class Namespace
  28554. *
  28555. * */
  28556. (function (Legend) {
  28557. /* *
  28558. *
  28559. * Declarations
  28560. *
  28561. * */
  28562. /* *
  28563. *
  28564. * Constants
  28565. *
  28566. * */
  28567. const composedMembers = [];
  28568. /* *
  28569. *
  28570. * Functions
  28571. *
  28572. * */
  28573. /**
  28574. * @private
  28575. */
  28576. function compose(ChartClass) {
  28577. if (U.pushUnique(composedMembers, ChartClass)) {
  28578. addEvent(ChartClass, 'beforeMargins', function () {
  28579. /**
  28580. * The legend contains an interactive overview over chart items,
  28581. * usually individual series or points depending on the series
  28582. * type. The color axis and bubble legend are also rendered in
  28583. * the chart legend.
  28584. *
  28585. * @name Highcharts.Chart#legend
  28586. * @type {Highcharts.Legend}
  28587. */
  28588. this.legend = new Legend(this, this.options.legend);
  28589. });
  28590. }
  28591. }
  28592. Legend.compose = compose;
  28593. })(Legend || (Legend = {}));
  28594. /* *
  28595. *
  28596. * Default Export
  28597. *
  28598. * */
  28599. /* *
  28600. *
  28601. * API Declarations
  28602. *
  28603. * */
  28604. /**
  28605. * @interface Highcharts.LegendItemObject
  28606. */ /**
  28607. * @name Highcharts.LegendItemObject#item
  28608. * @type {Highcharts.SVGElement|undefined}
  28609. */ /**
  28610. * @name Highcharts.LegendItemObject#line
  28611. * @type {Highcharts.SVGElement|undefined}
  28612. */ /**
  28613. * @name Highcharts.LegendItemObject#symbol
  28614. * @type {Highcharts.SVGElement|undefined}
  28615. */
  28616. /**
  28617. * Gets fired when the legend item belonging to a point is clicked. The default
  28618. * action is to toggle the visibility of the point. This can be prevented by
  28619. * returning `false` or calling `event.preventDefault()`.
  28620. *
  28621. * @callback Highcharts.PointLegendItemClickCallbackFunction
  28622. *
  28623. * @param {Highcharts.Point} this
  28624. * The point on which the event occured.
  28625. *
  28626. * @param {Highcharts.PointLegendItemClickEventObject} event
  28627. * The event that occured.
  28628. */
  28629. /**
  28630. * Information about the legend click event.
  28631. *
  28632. * @interface Highcharts.PointLegendItemClickEventObject
  28633. */ /**
  28634. * Related browser event.
  28635. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  28636. * @type {Highcharts.PointerEvent}
  28637. */ /**
  28638. * Prevent the default action of toggle the visibility of the point.
  28639. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  28640. * @type {Function}
  28641. */ /**
  28642. * Related point.
  28643. * @name Highcharts.PointLegendItemClickEventObject#target
  28644. * @type {Highcharts.Point}
  28645. */ /**
  28646. * Event type.
  28647. * @name Highcharts.PointLegendItemClickEventObject#type
  28648. * @type {"legendItemClick"}
  28649. */
  28650. /**
  28651. * Series color as used by the legend and some series types.
  28652. * @name Highcharts.Series#color
  28653. * @type {Highcharts.ColorType|undefined}
  28654. */ /**
  28655. * Legend data for the series.
  28656. * @name Highcharts.Series#legendItem
  28657. * @type {Highcharts.LegendItemObject|undefined}
  28658. * @since 10.3.0
  28659. */
  28660. /**
  28661. * Gets fired when the legend item belonging to a series is clicked. The default
  28662. * action is to toggle the visibility of the series. This can be prevented by
  28663. * returning `false` or calling `event.preventDefault()`.
  28664. *
  28665. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  28666. *
  28667. * @param {Highcharts.Series} this
  28668. * The series where the event occured.
  28669. *
  28670. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  28671. * The event that occured.
  28672. */
  28673. /**
  28674. * Information about the legend click event.
  28675. *
  28676. * @interface Highcharts.SeriesLegendItemClickEventObject
  28677. */ /**
  28678. * Related browser event.
  28679. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  28680. * @type {Highcharts.PointerEvent}
  28681. */ /**
  28682. * Prevent the default action of toggle the visibility of the series.
  28683. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  28684. * @type {Function}
  28685. */ /**
  28686. * Related series.
  28687. * @name Highcharts.SeriesLegendItemClickEventObject#target
  28688. * @type {Highcharts.Series}
  28689. */ /**
  28690. * Event type.
  28691. * @name Highcharts.SeriesLegendItemClickEventObject#type
  28692. * @type {"legendItemClick"}
  28693. */
  28694. (''); // keeps doclets above in JS file
  28695. return Legend;
  28696. });
  28697. _registerModule(_modules, 'Core/Legend/LegendSymbol.js', [_modules['Core/Utilities.js']], function (U) {
  28698. /* *
  28699. *
  28700. * (c) 2010-2021 Torstein Honsi
  28701. *
  28702. * License: www.highcharts.com/license
  28703. *
  28704. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28705. *
  28706. * */
  28707. const { extend, merge, pick } = U;
  28708. /* *
  28709. *
  28710. * Namespace
  28711. *
  28712. * */
  28713. var LegendSymbol;
  28714. (function (LegendSymbol) {
  28715. /* *
  28716. *
  28717. * Functions
  28718. *
  28719. * */
  28720. /* eslint-disable valid-jsdoc */
  28721. /**
  28722. * Get the series' symbol in the legend.
  28723. *
  28724. * This method should be overridable to create custom symbols through
  28725. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28726. *
  28727. * @private
  28728. * @function Highcharts.LegendSymbolMixin.lineMarker
  28729. *
  28730. * @param {Highcharts.Legend} legend
  28731. * The legend object.
  28732. */
  28733. function lineMarker(legend, item) {
  28734. const legendItem = this.legendItem = this.legendItem || {}, options = this.options, symbolWidth = legend.symbolWidth, symbolHeight = legend.symbolHeight, generalRadius = symbolHeight / 2, renderer = this.chart.renderer, legendItemGroup = legendItem.group, verticalCenter = legend.baseline -
  28735. Math.round(legend.fontMetrics.b * 0.3);
  28736. let attr = {}, legendSymbol, markerOptions = options.marker, lineSizer = 0;
  28737. // Draw the line
  28738. if (!this.chart.styledMode) {
  28739. attr = {
  28740. 'stroke-width': Math.min(options.lineWidth || 0, 24)
  28741. };
  28742. if (options.dashStyle) {
  28743. attr.dashstyle = options.dashStyle;
  28744. }
  28745. else if (options.linecap !== 'square') {
  28746. attr['stroke-linecap'] = 'round';
  28747. }
  28748. }
  28749. legendItem.line = renderer
  28750. .path()
  28751. .addClass('highcharts-graph')
  28752. .attr(attr)
  28753. .add(legendItemGroup);
  28754. if (attr['stroke-linecap']) {
  28755. lineSizer = Math.min(legendItem.line.strokeWidth(), symbolWidth) / 2;
  28756. }
  28757. if (symbolWidth) {
  28758. legendItem.line
  28759. .attr({
  28760. d: [
  28761. ['M', lineSizer, verticalCenter],
  28762. ['L', symbolWidth - lineSizer, verticalCenter]
  28763. ]
  28764. });
  28765. }
  28766. // Draw the marker
  28767. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  28768. // Do not allow the marker to be larger than the symbolHeight
  28769. let radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  28770. // Restrict symbol markers size
  28771. if (this.symbol.indexOf('url') === 0) {
  28772. markerOptions = merge(markerOptions, {
  28773. width: symbolHeight,
  28774. height: symbolHeight
  28775. });
  28776. radius = 0;
  28777. }
  28778. legendItem.symbol = legendSymbol = renderer
  28779. .symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, extend({ context: 'legend' }, markerOptions))
  28780. .addClass('highcharts-point')
  28781. .add(legendItemGroup);
  28782. legendSymbol.isMarker = true;
  28783. }
  28784. }
  28785. LegendSymbol.lineMarker = lineMarker;
  28786. /**
  28787. * Get the series' symbol in the legend.
  28788. *
  28789. * This method should be overridable to create custom symbols through
  28790. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28791. *
  28792. * @private
  28793. * @function Highcharts.LegendSymbolMixin.rectangle
  28794. *
  28795. * @param {Highcharts.Legend} legend
  28796. * The legend object
  28797. *
  28798. * @param {Highcharts.Point|Highcharts.Series} item
  28799. * The series (this) or point
  28800. */
  28801. function rectangle(legend, item) {
  28802. const legendItem = item.legendItem || {}, options = legend.options, symbolHeight = legend.symbolHeight, square = options.squareSymbol, symbolWidth = square ? symbolHeight : legend.symbolWidth;
  28803. legendItem.symbol = this.chart.renderer
  28804. .rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  28805. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  28806. .addClass('highcharts-point')
  28807. .attr({
  28808. zIndex: 3
  28809. })
  28810. .add(legendItem.group);
  28811. }
  28812. LegendSymbol.rectangle = rectangle;
  28813. })(LegendSymbol || (LegendSymbol = {}));
  28814. /* *
  28815. *
  28816. * Default Export
  28817. *
  28818. * */
  28819. return LegendSymbol;
  28820. });
  28821. _registerModule(_modules, 'Core/Series/SeriesDefaults.js', [], function () {
  28822. /* *
  28823. *
  28824. * (c) 2010-2021 Torstein Honsi
  28825. *
  28826. * License: www.highcharts.com/license
  28827. *
  28828. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28829. *
  28830. * */
  28831. /* *
  28832. *
  28833. * API Options
  28834. *
  28835. * */
  28836. /**
  28837. * General options for all series types.
  28838. *
  28839. * @optionparent plotOptions.series
  28840. */
  28841. const seriesDefaults = {
  28842. // base series options
  28843. /**
  28844. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  28845. * of a line graph. Round means that lines are rounded in the ends and
  28846. * bends.
  28847. *
  28848. * @type {Highcharts.SeriesLinecapValue}
  28849. * @default round
  28850. * @since 3.0.7
  28851. * @apioption plotOptions.line.linecap
  28852. */
  28853. /**
  28854. * Pixel width of the graph line.
  28855. *
  28856. * @see In styled mode, the line stroke-width can be set with the
  28857. * `.highcharts-graph` class name.
  28858. *
  28859. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  28860. * On all series
  28861. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  28862. * On one single series
  28863. *
  28864. * @product highcharts highstock
  28865. */
  28866. lineWidth: 1,
  28867. /**
  28868. * For some series, there is a limit that shuts down animation
  28869. * by default when the total number of points in the chart is too high.
  28870. * For example, for a column chart and its derivatives, animation does
  28871. * not run if there is more than 250 points totally. To disable this
  28872. * cap, set `animationLimit` to `Infinity`. This option works if animation
  28873. * is fired on individual points, not on a group of points like e.g. during
  28874. * the initial animation.
  28875. *
  28876. * @sample {highcharts} highcharts/plotoptions/series-animationlimit/
  28877. * Animation limit on updating individual points
  28878. *
  28879. * @type {number}
  28880. * @apioption plotOptions.series.animationLimit
  28881. */
  28882. /**
  28883. * Allow this series' points to be selected by clicking on the graphic
  28884. * (columns, point markers, pie slices, map areas etc).
  28885. *
  28886. * The selected points can be handled by point select and unselect
  28887. * events, or collectively by the [getSelectedPoints
  28888. * ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
  28889. *
  28890. * And alternative way of selecting points is through dragging.
  28891. *
  28892. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  28893. * Line
  28894. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  28895. * Column
  28896. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  28897. * Pie
  28898. * @sample {highcharts} highcharts/chart/events-selection-points/
  28899. * Select a range of points through a drag selection
  28900. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28901. * Map area
  28902. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  28903. * Map bubble
  28904. *
  28905. * @since 1.2.0
  28906. *
  28907. * @private
  28908. */
  28909. allowPointSelect: false,
  28910. /**
  28911. * When true, each point or column edge is rounded to its nearest pixel
  28912. * in order to render sharp on screen. In some cases, when there are a
  28913. * lot of densely packed columns, this leads to visible difference
  28914. * in column widths or distance between columns. In these cases,
  28915. * setting `crisp` to `false` may look better, even though each column
  28916. * is rendered blurry.
  28917. *
  28918. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  28919. * Crisp is false
  28920. *
  28921. * @since 5.0.10
  28922. * @product highcharts highstock gantt
  28923. *
  28924. * @private
  28925. */
  28926. crisp: true,
  28927. /**
  28928. * If true, a checkbox is displayed next to the legend item to allow
  28929. * selecting the series. The state of the checkbox is determined by
  28930. * the `selected` option.
  28931. *
  28932. * @productdesc {highmaps}
  28933. * Note that if a `colorAxis` is defined, the color axis is represented
  28934. * in the legend, not the series.
  28935. *
  28936. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  28937. * Show select box
  28938. *
  28939. * @since 1.2.0
  28940. *
  28941. * @private
  28942. */
  28943. showCheckbox: false,
  28944. /**
  28945. * Enable or disable the initial animation when a series is displayed.
  28946. * The animation can also be set as a configuration object. Please
  28947. * note that this option only applies to the initial animation of the
  28948. * series itself. For other animations, see [chart.animation](
  28949. * #chart.animation) and the animation parameter under the API methods.
  28950. * The following properties are supported:
  28951. *
  28952. * - `defer`: The animation delay time in milliseconds.
  28953. *
  28954. * - `duration`: The duration of the animation in milliseconds. (Defaults to
  28955. * `1000`)
  28956. *
  28957. * - `easing`: Can be a string reference to an easing function set on
  28958. * the `Math` object or a function. See the _Custom easing function_
  28959. * demo below. (Defaults to `easeInOutSine`)
  28960. *
  28961. * Due to poor performance, animation is disabled in old IE browsers
  28962. * for several chart types.
  28963. *
  28964. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  28965. * Animation disabled
  28966. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  28967. * Slower animation
  28968. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  28969. * Custom easing function
  28970. * @sample {highstock} stock/plotoptions/animation-slower/
  28971. * Slower animation
  28972. * @sample {highstock} stock/plotoptions/animation-easing/
  28973. * Custom easing function
  28974. * @sample {highmaps} maps/plotoptions/series-animation-true/
  28975. * Animation enabled on map series
  28976. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  28977. * Disabled on mapbubble series
  28978. *
  28979. * @type {boolean|Highcharts.AnimationOptionsObject}
  28980. * @default {highcharts} true
  28981. * @default {highstock} true
  28982. * @default {highmaps} false
  28983. *
  28984. * @private
  28985. */
  28986. animation: {
  28987. /** @ignore-option */
  28988. duration: 1000
  28989. },
  28990. /**
  28991. * An additional class name to apply to the series' graphical elements.
  28992. * This option does not replace default class names of the graphical
  28993. * element. Changes to the series' color will also be reflected in a
  28994. * chart's legend and tooltip.
  28995. *
  28996. * @sample {highcharts} highcharts/css/point-series-classname
  28997. *
  28998. * @type {string}
  28999. * @since 5.0.0
  29000. * @apioption plotOptions.series.className
  29001. */
  29002. /**
  29003. * Disable this option to allow series rendering in the whole plotting
  29004. * area.
  29005. *
  29006. * **Note:** Clipping should be always enabled when
  29007. * [chart.zoomType](#chart.zoomType) is set
  29008. *
  29009. * @sample {highcharts} highcharts/plotoptions/series-clip/
  29010. * Disabled clipping
  29011. *
  29012. * @default true
  29013. * @type {boolean}
  29014. * @since 3.0.0
  29015. * @apioption plotOptions.series.clip
  29016. */
  29017. /**
  29018. * The main color of the series. In line type series it applies to the
  29019. * line and the point markers unless otherwise specified. In bar type
  29020. * series it applies to the bars unless a color is specified per point.
  29021. * The default value is pulled from the `options.colors` array.
  29022. *
  29023. * In styled mode, the color can be defined by the
  29024. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  29025. * color can be set with the `.highcharts-series`,
  29026. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  29027. * `.highcharts-series-{n}` class, or individual classes given by the
  29028. * `className` option.
  29029. *
  29030. * @productdesc {highmaps}
  29031. * In maps, the series color is rarely used, as most choropleth maps use
  29032. * the color to denote the value of each point. The series color can
  29033. * however be used in a map with multiple series holding categorized
  29034. * data.
  29035. *
  29036. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  29037. * General plot option
  29038. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  29039. * One specific series
  29040. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  29041. * Area color
  29042. * @sample {highcharts} highcharts/series/infographic/
  29043. * Pattern fill
  29044. * @sample {highmaps} maps/demo/category-map/
  29045. * Category map by multiple series
  29046. *
  29047. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29048. * @apioption plotOptions.series.color
  29049. */
  29050. /**
  29051. * Styled mode only. A specific color index to use for the series, so its
  29052. * graphic representations are given the class name `highcharts-color-{n}`.
  29053. *
  29054. * Since v11, CSS variables on the form `--highcharts-color-{n}` make
  29055. * changing the color scheme very convenient.
  29056. *
  29057. * @sample {highcharts} highcharts/css/colorindex/ Series and point color
  29058. * index
  29059. *
  29060. * @type {number}
  29061. * @since 5.0.0
  29062. * @apioption plotOptions.series.colorIndex
  29063. */
  29064. /**
  29065. * Whether to connect a graph line across null points, or render a gap
  29066. * between the two points on either side of the null.
  29067. *
  29068. * In stacked area chart, if `connectNulls` is set to true,
  29069. * null points are interpreted as 0.
  29070. *
  29071. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  29072. * False by default
  29073. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  29074. * True
  29075. *
  29076. * @type {boolean}
  29077. * @default false
  29078. * @product highcharts highstock
  29079. * @apioption plotOptions.series.connectNulls
  29080. */
  29081. /**
  29082. * You can set the cursor to "pointer" if you have click events attached
  29083. * to the series, to signal to the user that the points and lines can
  29084. * be clicked.
  29085. *
  29086. * In styled mode, the series cursor can be set with the same classes
  29087. * as listed under [series.color](#plotOptions.series.color).
  29088. *
  29089. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  29090. * On line graph
  29091. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  29092. * On columns
  29093. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  29094. * On scatter markers
  29095. * @sample {highstock} stock/plotoptions/cursor/
  29096. * Pointer on a line graph
  29097. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  29098. * Map area
  29099. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  29100. * Map bubble
  29101. *
  29102. * @type {string|Highcharts.CursorValue}
  29103. * @apioption plotOptions.series.cursor
  29104. */
  29105. /**
  29106. * A reserved subspace to store options and values for customized
  29107. * functionality. Here you can add additional data for your own event
  29108. * callbacks and formatter callbacks.
  29109. *
  29110. * @sample {highcharts} highcharts/point/custom/
  29111. * Point and series with custom data
  29112. *
  29113. * @type {Highcharts.Dictionary<*>}
  29114. * @apioption plotOptions.series.custom
  29115. */
  29116. /**
  29117. * Name of the dash style to use for the graph, or for some series types
  29118. * the outline of each shape.
  29119. *
  29120. * In styled mode, the
  29121. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  29122. * can be set with the same classes as listed under
  29123. * [series.color](#plotOptions.series.color).
  29124. *
  29125. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  29126. * Possible values demonstrated
  29127. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  29128. * Chart suitable for printing in black and white
  29129. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  29130. * Possible values demonstrated
  29131. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  29132. * Possible values demonstrated
  29133. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  29134. * Dotted borders on a map
  29135. *
  29136. * @type {Highcharts.DashStyleValue}
  29137. * @default Solid
  29138. * @since 2.1
  29139. * @apioption plotOptions.series.dashStyle
  29140. */
  29141. /**
  29142. * A description of the series to add to the screen reader information
  29143. * about the series.
  29144. *
  29145. * @type {string}
  29146. * @since 5.0.0
  29147. * @requires modules/accessibility
  29148. * @apioption plotOptions.series.description
  29149. */
  29150. /**
  29151. * Options for the series data sorting.
  29152. *
  29153. * @type {Highcharts.DataSortingOptionsObject}
  29154. * @since 8.0.0
  29155. * @product highcharts highstock
  29156. * @apioption plotOptions.series.dataSorting
  29157. */
  29158. /**
  29159. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  29160. * #xAxis.reversed) to change the sorting order.
  29161. *
  29162. * @sample {highcharts} highcharts/datasorting/animation/
  29163. * Data sorting in scatter-3d
  29164. * @sample {highcharts} highcharts/datasorting/labels-animation/
  29165. * Axis labels animation
  29166. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  29167. * Dependent series sorting
  29168. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  29169. * Independent series sorting
  29170. *
  29171. * @type {boolean}
  29172. * @since 8.0.0
  29173. * @apioption plotOptions.series.dataSorting.enabled
  29174. */
  29175. /**
  29176. * Whether to allow matching points by name in an update. If this option
  29177. * is disabled, points will be matched by order.
  29178. *
  29179. * @sample {highcharts} highcharts/datasorting/match-by-name/
  29180. * Enabled match by name
  29181. *
  29182. * @type {boolean}
  29183. * @since 8.0.0
  29184. * @apioption plotOptions.series.dataSorting.matchByName
  29185. */
  29186. /**
  29187. * Determines what data value should be used to sort by.
  29188. *
  29189. * @sample {highcharts} highcharts/datasorting/sort-key/
  29190. * Sort key as `z` value
  29191. *
  29192. * @type {string}
  29193. * @since 8.0.0
  29194. * @default y
  29195. * @apioption plotOptions.series.dataSorting.sortKey
  29196. */
  29197. /**
  29198. * Enable or disable the mouse tracking for a specific series. This
  29199. * includes point tooltips and click events on graphs and points. For
  29200. * large datasets it improves performance.
  29201. *
  29202. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  29203. * No mouse tracking
  29204. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  29205. * No mouse tracking
  29206. *
  29207. * @type {boolean}
  29208. * @default true
  29209. * @apioption plotOptions.series.enableMouseTracking
  29210. */
  29211. enableMouseTracking: true,
  29212. /**
  29213. * Whether to use the Y extremes of the total chart width or only the
  29214. * zoomed area when zooming in on parts of the X axis. By default, the
  29215. * Y axis adjusts to the min and max of the visible data. Cartesian
  29216. * series only.
  29217. *
  29218. * @type {boolean}
  29219. * @default false
  29220. * @since 4.1.6
  29221. * @product highcharts highstock gantt
  29222. * @apioption plotOptions.series.getExtremesFromAll
  29223. */
  29224. /**
  29225. * An array specifying which option maps to which key in the data point
  29226. * array. This makes it convenient to work with unstructured data arrays
  29227. * from different sources.
  29228. *
  29229. * @see [series.data](#series.line.data)
  29230. *
  29231. * @sample {highcharts|highstock} highcharts/series/data-keys/
  29232. * An extended data array with keys
  29233. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  29234. * Nested keys used to access object properties
  29235. *
  29236. * @type {Array<string>}
  29237. * @since 4.1.6
  29238. * @apioption plotOptions.series.keys
  29239. */
  29240. /**
  29241. * The line cap used for line ends and line joins on the graph.
  29242. *
  29243. * @sample highcharts/series-line/linecap/
  29244. * Line cap comparison
  29245. *
  29246. * @type {Highcharts.SeriesLinecapValue}
  29247. * @default round
  29248. * @product highcharts highstock
  29249. * @apioption plotOptions.series.linecap
  29250. */
  29251. /**
  29252. * The [id](#series.id) of another series to link to. Additionally,
  29253. * the value can be ":previous" to link to the previous series. When
  29254. * two series are linked, only the first one appears in the legend.
  29255. * Toggling the visibility of this also toggles the linked series.
  29256. *
  29257. * If master series uses data sorting and linked series does not have
  29258. * its own sorting definition, the linked series will be sorted in the
  29259. * same order as the master one.
  29260. *
  29261. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  29262. * Linked series
  29263. *
  29264. * @type {string}
  29265. * @since 3.0
  29266. * @product highcharts highstock gantt
  29267. * @apioption plotOptions.series.linkedTo
  29268. */
  29269. /**
  29270. * Options for the corresponding navigator series if `showInNavigator`
  29271. * is `true` for this series. Available options are the same as any
  29272. * series, documented at [plotOptions](#plotOptions.series) and
  29273. * [series](#series).
  29274. *
  29275. * These options are merged with options in [navigator.series](
  29276. * #navigator.series), and will take precedence if the same option is
  29277. * defined both places.
  29278. *
  29279. * @see [navigator.series](#navigator.series)
  29280. *
  29281. * @type {Highcharts.PlotSeriesOptions}
  29282. * @since 5.0.0
  29283. * @product highstock
  29284. * @apioption plotOptions.series.navigatorOptions
  29285. */
  29286. /**
  29287. * The color for the parts of the graph or points that are below the
  29288. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  29289. * precedence over the negative color. Using `negativeColor` is
  29290. * equivalent to applying a zone with value of 0.
  29291. *
  29292. * @see In styled mode, a negative color is applied by setting this option
  29293. * to `true` combined with the `.highcharts-negative` class name.
  29294. *
  29295. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  29296. * Spline, area and column
  29297. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  29298. * Arearange
  29299. * @sample {highcharts} highcharts/css/series-negative-color/
  29300. * Styled mode
  29301. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  29302. * Spline, area and column
  29303. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  29304. * Arearange
  29305. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  29306. * Spline, area and column
  29307. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  29308. * Arearange
  29309. *
  29310. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29311. * @since 3.0
  29312. * @apioption plotOptions.series.negativeColor
  29313. */
  29314. /**
  29315. * Same as
  29316. * [accessibility.point.descriptionFormat](#accessibility.point.descriptionFormat),
  29317. * but for an individual series. Overrides the chart wide configuration.
  29318. *
  29319. * @type {Function}
  29320. * @since 11.1.0
  29321. * @apioption plotOptions.series.pointDescriptionFormat
  29322. */
  29323. /**
  29324. * Same as
  29325. * [accessibility.series.descriptionFormatter](#accessibility.series.descriptionFormatter),
  29326. * but for an individual series. Overrides the chart wide configuration.
  29327. *
  29328. * @type {Function}
  29329. * @since 5.0.12
  29330. * @apioption plotOptions.series.pointDescriptionFormatter
  29331. */
  29332. /**
  29333. * If no x values are given for the points in a series, `pointInterval`
  29334. * defines the interval of the x values. For example, if a series
  29335. * contains one value every decade starting from year 0, set
  29336. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  29337. * is set in milliseconds.
  29338. *
  29339. * It can be also be combined with `pointIntervalUnit` to draw irregular
  29340. * time intervals.
  29341. *
  29342. * If combined with `relativeXValue`, an x value can be set on each
  29343. * point, and the `pointInterval` is added x times to the `pointStart`
  29344. * setting.
  29345. *
  29346. * Please note that this options applies to the _series data_, not the
  29347. * interval of the axis ticks, which is independent.
  29348. *
  29349. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29350. * Datetime X axis
  29351. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29352. * Relative x value
  29353. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29354. * Using pointStart and pointInterval
  29355. * @sample {highstock} stock/plotoptions/relativexvalue/
  29356. * Relative x value
  29357. *
  29358. * @type {number}
  29359. * @default 1
  29360. * @product highcharts highstock gantt
  29361. * @apioption plotOptions.series.pointInterval
  29362. */
  29363. /**
  29364. * On datetime series, this allows for setting the
  29365. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  29366. * units, `day`, `month` and `year`. A day is usually the same as 24
  29367. * hours, but `pointIntervalUnit` also takes the DST crossover into
  29368. * consideration when dealing with local time. Combine this option with
  29369. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  29370. *
  29371. * Please note that this options applies to the _series data_, not the
  29372. * interval of the axis ticks, which is independent.
  29373. *
  29374. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  29375. * One point a month
  29376. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  29377. * One point a month
  29378. *
  29379. * @type {string}
  29380. * @since 4.1.0
  29381. * @product highcharts highstock gantt
  29382. * @validvalue ["day", "month", "year"]
  29383. * @apioption plotOptions.series.pointIntervalUnit
  29384. */
  29385. /**
  29386. * Possible values: `"on"`, `"between"`, `number`.
  29387. *
  29388. * In a column chart, when pointPlacement is `"on"`, the point will not
  29389. * create any padding of the X axis. In a polar column chart this means
  29390. * that the first column points directly north. If the pointPlacement is
  29391. * `"between"`, the columns will be laid out between ticks. This is
  29392. * useful for example for visualising an amount between two points in
  29393. * time or in a certain sector of a polar chart.
  29394. *
  29395. * Since Highcharts 3.0.2, the point placement can also be numeric,
  29396. * where 0 is on the axis value, -0.5 is between this value and the
  29397. * previous, and 0.5 is between this value and the next. Unlike the
  29398. * textual options, numeric point placement options won't affect axis
  29399. * padding.
  29400. *
  29401. * Note that pointPlacement needs a [pointRange](
  29402. * #plotOptions.series.pointRange) to work. For column series this is
  29403. * computed, but for line-type series it needs to be set.
  29404. *
  29405. * For the `xrange` series type and gantt charts, if the Y axis is a
  29406. * category axis, the `pointPlacement` applies to the Y axis rather than
  29407. * the (typically datetime) X axis.
  29408. *
  29409. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  29410. * charts.
  29411. *
  29412. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  29413. *
  29414. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  29415. * Between in a column chart
  29416. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  29417. * Numeric placement for custom layout
  29418. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  29419. * Placement in heatmap
  29420. *
  29421. * @type {string|number}
  29422. * @since 2.3.0
  29423. * @product highcharts highstock gantt
  29424. * @apioption plotOptions.series.pointPlacement
  29425. */
  29426. /**
  29427. * If no x values are given for the points in a series, pointStart
  29428. * defines on what value to start. For example, if a series contains one
  29429. * yearly value starting from 1945, set pointStart to 1945.
  29430. *
  29431. * If combined with `relativeXValue`, an x value can be set on each
  29432. * point. The x value from the point options is multiplied by
  29433. * `pointInterval` and added to `pointStart` to produce a modified x
  29434. * value.
  29435. *
  29436. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  29437. * Linear
  29438. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29439. * Datetime
  29440. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29441. * Relative x value
  29442. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29443. * Using pointStart and pointInterval
  29444. * @sample {highstock} stock/plotoptions/relativexvalue/
  29445. * Relative x value
  29446. *
  29447. * @type {number}
  29448. * @default 0
  29449. * @product highcharts highstock gantt
  29450. * @apioption plotOptions.series.pointStart
  29451. */
  29452. /**
  29453. * When true, X values in the data set are relative to the current
  29454. * `pointStart`, `pointInterval` and `pointIntervalUnit` settings. This
  29455. * allows compression of the data for datasets with irregular X values.
  29456. *
  29457. * The real X values are computed on the formula `f(x) = ax + b`, where
  29458. * `a` is the `pointInterval` (optionally with a time unit given by
  29459. * `pointIntervalUnit`), and `b` is the `pointStart`.
  29460. *
  29461. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29462. * Relative X value
  29463. * @sample {highstock} stock/plotoptions/relativexvalue/
  29464. * Relative X value
  29465. *
  29466. * @type {boolean}
  29467. * @default false
  29468. * @product highcharts highstock
  29469. * @apioption plotOptions.series.relativeXValue
  29470. */
  29471. /**
  29472. * Whether to select the series initially. If `showCheckbox` is true,
  29473. * the checkbox next to the series name in the legend will be checked
  29474. * for a selected series.
  29475. *
  29476. * @sample {highcharts} highcharts/plotoptions/series-selected/
  29477. * One out of two series selected
  29478. *
  29479. * @type {boolean}
  29480. * @default false
  29481. * @since 1.2.0
  29482. * @apioption plotOptions.series.selected
  29483. */
  29484. /**
  29485. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  29486. * shadow can be an object configuration containing `color`, `offsetX`,
  29487. * `offsetY`, `opacity` and `width`.
  29488. *
  29489. * Note that in some cases, like stacked columns or other dense layouts, the
  29490. * series may cast shadows on each other. In that case, the
  29491. * `chart.seriesGroupShadow` allows applying a common drop shadow to the
  29492. * whole series group.
  29493. *
  29494. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  29495. * Shadow enabled
  29496. *
  29497. * @type {boolean|Highcharts.ShadowOptionsObject}
  29498. * @default false
  29499. * @apioption plotOptions.series.shadow
  29500. */
  29501. /**
  29502. * Whether to display this particular series or series type in the
  29503. * legend. Standalone series are shown in legend by default, and linked
  29504. * series are not. Since v7.2.0 it is possible to show series that use
  29505. * colorAxis by setting this option to `true`.
  29506. *
  29507. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  29508. * One series in the legend, one hidden
  29509. *
  29510. * @type {boolean}
  29511. * @apioption plotOptions.series.showInLegend
  29512. */
  29513. /**
  29514. * Whether or not to show the series in the navigator. Takes precedence
  29515. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  29516. *
  29517. * @type {boolean}
  29518. * @since 5.0.0
  29519. * @product highstock
  29520. * @apioption plotOptions.series.showInNavigator
  29521. */
  29522. /**
  29523. * If set to `true`, the accessibility module will skip past the points
  29524. * in this series for keyboard navigation.
  29525. *
  29526. * @type {boolean}
  29527. * @since 5.0.12
  29528. * @apioption plotOptions.series.skipKeyboardNavigation
  29529. */
  29530. /**
  29531. * Whether to stack the values of each series on top of each other.
  29532. * Possible values are `undefined` to disable, `"normal"` to stack by
  29533. * value or `"percent"`.
  29534. *
  29535. * When stacking is enabled, data must be sorted
  29536. * in ascending X order.
  29537. *
  29538. * Some stacking options are related to specific series types. In the
  29539. * streamgraph series type, the stacking option is set to `"stream"`.
  29540. * The second one is `"overlap"`, which only applies to waterfall
  29541. * series.
  29542. *
  29543. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  29544. *
  29545. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  29546. * Line
  29547. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  29548. * Column
  29549. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  29550. * Bar
  29551. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  29552. * Area
  29553. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  29554. * Line
  29555. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  29556. * Column
  29557. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  29558. * Bar
  29559. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  29560. * Area
  29561. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  29562. * Waterfall with normal stacking
  29563. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  29564. * Waterfall with overlap stacking
  29565. * @sample {highstock} stock/plotoptions/stacking/
  29566. * Area
  29567. *
  29568. * @type {string}
  29569. * @product highcharts highstock
  29570. * @validvalue ["normal", "overlap", "percent", "stream"]
  29571. * @apioption plotOptions.series.stacking
  29572. */
  29573. /**
  29574. * Whether to apply steps to the line. Possible values are `left`,
  29575. * `center` and `right`.
  29576. *
  29577. * @sample {highcharts} highcharts/plotoptions/line-step/
  29578. * Different step line options
  29579. * @sample {highcharts} highcharts/plotoptions/area-step/
  29580. * Stepped, stacked area
  29581. * @sample {highstock} stock/plotoptions/line-step/
  29582. * Step line
  29583. *
  29584. * @type {string}
  29585. * @since 1.2.5
  29586. * @product highcharts highstock
  29587. * @validvalue ["left", "center", "right"]
  29588. * @apioption plotOptions.series.step
  29589. */
  29590. /**
  29591. * The threshold, also called zero level or base level. For line type
  29592. * series this is only used in conjunction with
  29593. * [negativeColor](#plotOptions.series.negativeColor).
  29594. *
  29595. * @see [softThreshold](#plotOptions.series.softThreshold).
  29596. *
  29597. * @type {number|null}
  29598. * @default 0
  29599. * @since 3.0
  29600. * @product highcharts highstock
  29601. * @apioption plotOptions.series.threshold
  29602. */
  29603. /**
  29604. * Set the initial visibility of the series.
  29605. *
  29606. * @sample {highcharts} highcharts/plotoptions/series-visible/
  29607. * Two series, one hidden and one visible
  29608. * @sample {highstock} stock/plotoptions/series-visibility/
  29609. * Hidden series
  29610. *
  29611. * @type {boolean}
  29612. * @default true
  29613. * @apioption plotOptions.series.visible
  29614. */
  29615. /**
  29616. * Defines the Axis on which the zones are applied.
  29617. *
  29618. * @see [zones](#plotOptions.series.zones)
  29619. *
  29620. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  29621. * Zones on the X-Axis
  29622. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  29623. * Zones on the X-Axis
  29624. *
  29625. * @type {string}
  29626. * @default y
  29627. * @since 4.1.0
  29628. * @product highcharts highstock
  29629. * @apioption plotOptions.series.zoneAxis
  29630. */
  29631. /**
  29632. * General event handlers for the series items. These event hooks can
  29633. * also be attached to the series at run time using the
  29634. * `Highcharts.addEvent` function.
  29635. *
  29636. * @declare Highcharts.SeriesEventsOptionsObject
  29637. *
  29638. * @private
  29639. */
  29640. events: {},
  29641. /**
  29642. * Fires after the series has finished its initial animation, or in case
  29643. * animation is disabled, immediately as the series is displayed.
  29644. *
  29645. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  29646. * Show label after animate
  29647. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  29648. * Show label after animate
  29649. *
  29650. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  29651. * @since 4.0
  29652. * @product highcharts highstock gantt
  29653. * @context Highcharts.Series
  29654. * @apioption plotOptions.series.events.afterAnimate
  29655. */
  29656. /**
  29657. * Fires when the checkbox next to the series' name in the legend is
  29658. * clicked. One parameter, `event`, is passed to the function. The state
  29659. * of the checkbox is found by `event.checked`. The checked item is
  29660. * found by `event.item`. Return `false` to prevent the default action
  29661. * which is to toggle the select state of the series.
  29662. *
  29663. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  29664. * Alert checkbox status
  29665. *
  29666. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  29667. * @since 1.2.0
  29668. * @context Highcharts.Series
  29669. * @apioption plotOptions.series.events.checkboxClick
  29670. */
  29671. /**
  29672. * Fires when the series is clicked. One parameter, `event`, is passed
  29673. * to the function, containing common event information. Additionally,
  29674. * `event.point` holds a pointer to the nearest point on the graph.
  29675. *
  29676. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  29677. * Alert click info
  29678. * @sample {highstock} stock/plotoptions/series-events-click/
  29679. * Alert click info
  29680. * @sample {highmaps} maps/plotoptions/series-events-click/
  29681. * Display click info in subtitle
  29682. *
  29683. * @type {Highcharts.SeriesClickCallbackFunction}
  29684. * @context Highcharts.Series
  29685. * @apioption plotOptions.series.events.click
  29686. */
  29687. /**
  29688. * Fires when the series is hidden after chart generation time, either
  29689. * by clicking the legend item or by calling `.hide()`.
  29690. *
  29691. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  29692. * Alert when the series is hidden by clicking the legend item
  29693. *
  29694. * @type {Highcharts.SeriesHideCallbackFunction}
  29695. * @since 1.2.0
  29696. * @context Highcharts.Series
  29697. * @apioption plotOptions.series.events.hide
  29698. */
  29699. /**
  29700. * Fires when the legend item belonging to the series is clicked. One
  29701. * parameter, `event`, is passed to the function. The default action
  29702. * is to toggle the visibility of the series. This can be prevented
  29703. * by returning `false` or calling `event.preventDefault()`.
  29704. *
  29705. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  29706. * Confirm hiding and showing
  29707. *
  29708. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  29709. * @context Highcharts.Series
  29710. * @apioption plotOptions.series.events.legendItemClick
  29711. */
  29712. /**
  29713. * Fires when the mouse leaves the graph. One parameter, `event`, is
  29714. * passed to the function, containing common event information. If the
  29715. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  29716. * doesn't happen before the mouse enters another graph or leaves the
  29717. * plot area.
  29718. *
  29719. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29720. * With sticky tracking by default
  29721. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29722. * Without sticky tracking
  29723. *
  29724. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  29725. * @context Highcharts.Series
  29726. * @apioption plotOptions.series.events.mouseOut
  29727. */
  29728. /**
  29729. * Fires when the mouse enters the graph. One parameter, `event`, is
  29730. * passed to the function, containing common event information.
  29731. *
  29732. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29733. * With sticky tracking by default
  29734. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29735. * Without sticky tracking
  29736. *
  29737. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  29738. * @context Highcharts.Series
  29739. * @apioption plotOptions.series.events.mouseOver
  29740. */
  29741. /**
  29742. * Fires when the series is shown after chart generation time, either
  29743. * by clicking the legend item or by calling `.show()`.
  29744. *
  29745. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  29746. * Alert when the series is shown by clicking the legend item.
  29747. *
  29748. * @type {Highcharts.SeriesShowCallbackFunction}
  29749. * @since 1.2.0
  29750. * @context Highcharts.Series
  29751. * @apioption plotOptions.series.events.show
  29752. */
  29753. /**
  29754. * Options for the point markers of line and scatter-like series. Properties
  29755. * like `fillColor`, `lineColor` and `lineWidth` define the visual
  29756. * appearance of the markers. The `symbol` option defines the shape. Other
  29757. * series types, like column series, don't have markers, but have visual
  29758. * options on the series level instead.
  29759. *
  29760. * In styled mode, the markers can be styled with the `.highcharts-point`,
  29761. * `.highcharts-point-hover` and `.highcharts-point-select` class names.
  29762. *
  29763. * @declare Highcharts.PointMarkerOptionsObject
  29764. *
  29765. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29766. * Using the mapmarker symbol for points
  29767. *
  29768. * @private
  29769. */
  29770. marker: {
  29771. /**
  29772. * Enable or disable the point marker. If `undefined`, the markers
  29773. * are hidden when the data is dense, and shown for more widespread
  29774. * data points.
  29775. *
  29776. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  29777. * Disabled markers
  29778. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  29779. * Disabled in normal state but enabled on hover
  29780. * @sample {highstock} stock/plotoptions/series-marker/
  29781. * Enabled markers
  29782. *
  29783. * @type {boolean}
  29784. * @default {highcharts} undefined
  29785. * @default {highstock} false
  29786. * @apioption plotOptions.series.marker.enabled
  29787. */
  29788. /**
  29789. * The threshold for how dense the point markers should be before
  29790. * they are hidden, given that `enabled` is not defined. The number
  29791. * indicates the horizontal distance between the two closest points
  29792. * in the series, as multiples of the `marker.radius`. In other
  29793. * words, the default value of 2 means points are hidden if
  29794. * overlapping horizontally.
  29795. *
  29796. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  29797. * A higher threshold
  29798. *
  29799. * @since 6.0.5
  29800. */
  29801. enabledThreshold: 2,
  29802. /**
  29803. * The fill color of the point marker. When `undefined`, the series'
  29804. * or point's color is used.
  29805. *
  29806. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29807. * White fill
  29808. *
  29809. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29810. * @apioption plotOptions.series.marker.fillColor
  29811. */
  29812. /**
  29813. * Image markers only. Set the image width explicitly. When using
  29814. * this option, a `width` must also be set.
  29815. *
  29816. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29817. * Fixed width and height
  29818. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29819. * Fixed width and height
  29820. *
  29821. * @type {number}
  29822. * @since 4.0.4
  29823. * @apioption plotOptions.series.marker.height
  29824. */
  29825. /**
  29826. * The color of the point marker's outline. When `undefined`, the
  29827. * series' or point's color is used.
  29828. *
  29829. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29830. * Inherit from series color (undefined)
  29831. *
  29832. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29833. */
  29834. lineColor: "#ffffff" /* Palette.backgroundColor */,
  29835. /**
  29836. * The width of the point marker's outline.
  29837. *
  29838. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29839. * 2px blue marker
  29840. */
  29841. lineWidth: 0,
  29842. /**
  29843. * The radius of the point marker.
  29844. *
  29845. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  29846. * Bigger markers
  29847. *
  29848. * @default {highstock} 2
  29849. * @default {highcharts} 4
  29850. *
  29851. */
  29852. radius: 4,
  29853. /**
  29854. * A predefined shape or symbol for the marker. When undefined, the
  29855. * symbol is pulled from options.symbols. Other possible values are
  29856. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  29857. * `'triangle-down'`.
  29858. *
  29859. * Additionally, the URL to a graphic can be given on this form:
  29860. * `'url(graphic.png)'`. Note that for the image to be applied to
  29861. * exported charts, its URL needs to be accessible by the export
  29862. * server.
  29863. *
  29864. * Custom callbacks for symbol path generation can also be added to
  29865. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  29866. * used by its method name, as shown in the demo.
  29867. *
  29868. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  29869. * Predefined, graphic and custom markers
  29870. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  29871. * Predefined, graphic and custom markers
  29872. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29873. * Using the mapmarker symbol for points
  29874. *
  29875. * @type {string}
  29876. * @apioption plotOptions.series.marker.symbol
  29877. */
  29878. /**
  29879. * Image markers only. Set the image width explicitly. When using
  29880. * this option, a `height` must also be set.
  29881. *
  29882. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29883. * Fixed width and height
  29884. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29885. * Fixed width and height
  29886. *
  29887. * @type {number}
  29888. * @since 4.0.4
  29889. * @apioption plotOptions.series.marker.width
  29890. */
  29891. /**
  29892. * States for a single point marker.
  29893. *
  29894. * @declare Highcharts.PointStatesOptionsObject
  29895. */
  29896. states: {
  29897. /**
  29898. * The normal state of a single point marker. Currently only
  29899. * used for setting animation when returning to normal state
  29900. * from hover.
  29901. *
  29902. * @declare Highcharts.PointStatesNormalOptionsObject
  29903. */
  29904. normal: {
  29905. /**
  29906. * Animation when returning to normal state after hovering.
  29907. *
  29908. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29909. */
  29910. animation: true
  29911. },
  29912. /**
  29913. * The hover state for a single point marker.
  29914. *
  29915. * @declare Highcharts.PointStatesHoverOptionsObject
  29916. */
  29917. hover: {
  29918. /**
  29919. * Animation when hovering over the marker.
  29920. *
  29921. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29922. */
  29923. animation: {
  29924. /** @internal */
  29925. duration: 150
  29926. },
  29927. /**
  29928. * Enable or disable the point marker.
  29929. *
  29930. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  29931. * Disabled hover state
  29932. */
  29933. enabled: true,
  29934. /**
  29935. * The fill color of the marker in hover state. When
  29936. * `undefined`, the series' or point's fillColor for normal
  29937. * state is used.
  29938. *
  29939. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29940. * @apioption plotOptions.series.marker.states.hover.fillColor
  29941. */
  29942. /**
  29943. * The color of the point marker's outline. When
  29944. * `undefined`, the series' or point's lineColor for normal
  29945. * state is used.
  29946. *
  29947. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  29948. * White fill color, black line color
  29949. *
  29950. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29951. * @apioption plotOptions.series.marker.states.hover.lineColor
  29952. */
  29953. /**
  29954. * The width of the point marker's outline. When
  29955. * `undefined`, the series' or point's lineWidth for normal
  29956. * state is used.
  29957. *
  29958. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  29959. * 3px line width
  29960. *
  29961. * @type {number}
  29962. * @apioption plotOptions.series.marker.states.hover.lineWidth
  29963. */
  29964. /**
  29965. * The radius of the point marker. In hover state, it
  29966. * defaults to the normal state's radius + 2 as per the
  29967. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  29968. * option.
  29969. *
  29970. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  29971. * 10px radius
  29972. *
  29973. * @type {number}
  29974. * @apioption plotOptions.series.marker.states.hover.radius
  29975. */
  29976. /**
  29977. * The number of pixels to increase the radius of the
  29978. * hovered point.
  29979. *
  29980. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29981. * 5 pixels greater radius on hover
  29982. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29983. * 5 pixels greater radius on hover
  29984. *
  29985. * @since 4.0.3
  29986. */
  29987. radiusPlus: 2,
  29988. /**
  29989. * The additional line width for a hovered point.
  29990. *
  29991. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29992. * 2 pixels wider on hover
  29993. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29994. * 2 pixels wider on hover
  29995. *
  29996. * @since 4.0.3
  29997. */
  29998. lineWidthPlus: 1
  29999. },
  30000. /**
  30001. * The appearance of the point marker when selected. In order to
  30002. * allow a point to be selected, set the
  30003. * `series.allowPointSelect` option to true.
  30004. *
  30005. * @declare Highcharts.PointStatesSelectOptionsObject
  30006. */
  30007. select: {
  30008. /**
  30009. * Enable or disable visible feedback for selection.
  30010. *
  30011. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  30012. * Disabled select state
  30013. *
  30014. * @type {boolean}
  30015. * @default true
  30016. * @apioption plotOptions.series.marker.states.select.enabled
  30017. */
  30018. /**
  30019. * The radius of the point marker. In hover state, it
  30020. * defaults to the normal state's radius + 2.
  30021. *
  30022. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  30023. * 10px radius for selected points
  30024. *
  30025. * @type {number}
  30026. * @apioption plotOptions.series.marker.states.select.radius
  30027. */
  30028. /**
  30029. * The fill color of the point marker.
  30030. *
  30031. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  30032. * Solid red discs for selected points
  30033. *
  30034. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30035. */
  30036. fillColor: "#cccccc" /* Palette.neutralColor20 */,
  30037. /**
  30038. * The color of the point marker's outline. When
  30039. * `undefined`, the series' or point's color is used.
  30040. *
  30041. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  30042. * Red line color for selected points
  30043. *
  30044. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30045. */
  30046. lineColor: "#000000" /* Palette.neutralColor100 */,
  30047. /**
  30048. * The width of the point marker's outline.
  30049. *
  30050. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  30051. * 3px line width for selected points
  30052. */
  30053. lineWidth: 2
  30054. }
  30055. }
  30056. },
  30057. /**
  30058. * Properties for each single point.
  30059. *
  30060. * @declare Highcharts.PlotSeriesPointOptions
  30061. *
  30062. * @private
  30063. */
  30064. point: {
  30065. /**
  30066. * Fires when a point is clicked. One parameter, `event`, is passed
  30067. * to the function, containing common event information.
  30068. *
  30069. * If the `series.allowPointSelect` option is true, the default
  30070. * action for the point's click event is to toggle the point's
  30071. * select state. Returning `false` cancels this action.
  30072. *
  30073. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  30074. * Click marker to alert values
  30075. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  30076. * Click column
  30077. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  30078. * Go to URL
  30079. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  30080. * Click marker to display values
  30081. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  30082. * Go to URL
  30083. *
  30084. * @type {Highcharts.PointClickCallbackFunction}
  30085. * @context Highcharts.Point
  30086. * @apioption plotOptions.series.point.events.click
  30087. */
  30088. /**
  30089. * Fires when the mouse leaves the area close to the point. One
  30090. * parameter, `event`, is passed to the function, containing common
  30091. * event information.
  30092. *
  30093. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30094. * Show values in the chart's corner on mouse over
  30095. *
  30096. * @type {Highcharts.PointMouseOutCallbackFunction}
  30097. * @context Highcharts.Point
  30098. * @apioption plotOptions.series.point.events.mouseOut
  30099. */
  30100. /**
  30101. * Fires when the mouse enters the area close to the point. One
  30102. * parameter, `event`, is passed to the function, containing common
  30103. * event information.
  30104. *
  30105. * Returning `false` cancels the default behavior, which is to show a
  30106. * tooltip for the point.
  30107. *
  30108. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30109. * Show values in the chart's corner on mouse over
  30110. *
  30111. * @type {Highcharts.PointMouseOverCallbackFunction}
  30112. * @context Highcharts.Point
  30113. * @apioption plotOptions.series.point.events.mouseOver
  30114. */
  30115. /**
  30116. * Fires when the point is removed using the `.remove()` method. One
  30117. * parameter, `event`, is passed to the function. Returning `false`
  30118. * cancels the operation.
  30119. *
  30120. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  30121. * Remove point and confirm
  30122. *
  30123. * @type {Highcharts.PointRemoveCallbackFunction}
  30124. * @since 1.2.0
  30125. * @context Highcharts.Point
  30126. * @apioption plotOptions.series.point.events.remove
  30127. */
  30128. /**
  30129. * Fires when the point is selected either programmatically or
  30130. * following a click on the point. One parameter, `event`, is passed
  30131. * to the function. Returning `false` cancels the operation.
  30132. *
  30133. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  30134. * Report the last selected point
  30135. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30136. * Report select and unselect
  30137. *
  30138. * @type {Highcharts.PointSelectCallbackFunction}
  30139. * @since 1.2.0
  30140. * @context Highcharts.Point
  30141. * @apioption plotOptions.series.point.events.select
  30142. */
  30143. /**
  30144. * Fires when the point is unselected either programmatically or
  30145. * following a click on the point. One parameter, `event`, is passed
  30146. * to the function.
  30147. * Returning `false` cancels the operation.
  30148. *
  30149. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  30150. * Report the last unselected point
  30151. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30152. * Report select and unselect
  30153. *
  30154. * @type {Highcharts.PointUnselectCallbackFunction}
  30155. * @since 1.2.0
  30156. * @context Highcharts.Point
  30157. * @apioption plotOptions.series.point.events.unselect
  30158. */
  30159. /**
  30160. * Fires when the point is updated programmatically through the
  30161. * `.update()` method. One parameter, `event`, is passed to the
  30162. * function. The new point options can be accessed through
  30163. * `event.options`. Returning `false` cancels the operation.
  30164. *
  30165. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  30166. * Confirm point updating
  30167. *
  30168. * @type {Highcharts.PointUpdateCallbackFunction}
  30169. * @since 1.2.0
  30170. * @context Highcharts.Point
  30171. * @apioption plotOptions.series.point.events.update
  30172. */
  30173. /**
  30174. * Events for each single point.
  30175. *
  30176. * @declare Highcharts.PointEventsOptionsObject
  30177. */
  30178. events: {}
  30179. },
  30180. /**
  30181. * Options for the series data labels, appearing next to each data
  30182. * point.
  30183. *
  30184. * Since v6.2.0, multiple data labels can be applied to each single
  30185. * point by defining them as an array of configs.
  30186. *
  30187. * In styled mode, the data labels can be styled with the
  30188. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  30189. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  30190. *
  30191. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  30192. * Data labels enabled
  30193. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  30194. * Multiple data labels on a bar series
  30195. * @sample {highcharts} highcharts/css/series-datalabels
  30196. * Styled mode example
  30197. * @sample {highmaps} maps/demo/color-axis
  30198. * Choropleth map with data labels
  30199. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30200. * Using data labels as map markers
  30201. *
  30202. * @type {*|Array<*>}
  30203. * @product highcharts highstock highmaps gantt
  30204. *
  30205. * @private
  30206. */
  30207. dataLabels: {
  30208. /**
  30209. * Enable or disable the initial animation when a series is displayed
  30210. * for the `dataLabels`. The animation can also be set as a
  30211. * configuration object. Please note that this option only applies to
  30212. * the initial animation.
  30213. *
  30214. * For other animations, see [chart.animation](#chart.animation) and the
  30215. * animation parameter under the API methods. The following properties
  30216. * are supported:
  30217. *
  30218. * - `defer`: The animation delay time in milliseconds.
  30219. *
  30220. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  30221. * Animation defer settings
  30222. *
  30223. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30224. * @since 8.2.0
  30225. * @apioption plotOptions.series.dataLabels.animation
  30226. */
  30227. animation: {},
  30228. /**
  30229. * The animation delay time in milliseconds. Set to `0` to render the
  30230. * data labels immediately. As `undefined` inherits defer time from the
  30231. * [series.animation.defer](#plotOptions.series.animation.defer).
  30232. *
  30233. * @type {number}
  30234. * @since 8.2.0
  30235. * @apioption plotOptions.series.dataLabels.animation.defer
  30236. */
  30237. /**
  30238. * The alignment of the data label compared to the point. If `right`,
  30239. * the right side of the label should be touching the point. For points
  30240. * with an extent, like columns, the alignments also dictates how to
  30241. * align it inside the box, as given with the
  30242. * [inside](#plotOptions.column.dataLabels.inside) option. Can be one of
  30243. * `left`, `center` or `right`.
  30244. *
  30245. * @sample {highcharts}
  30246. * highcharts/plotoptions/series-datalabels-align-left/ Left
  30247. * aligned
  30248. * @sample {highcharts}
  30249. * highcharts/plotoptions/bar-datalabels-align-inside-bar/ Data
  30250. * labels inside the bar
  30251. *
  30252. * @type {Highcharts.AlignValue|null}
  30253. */
  30254. align: 'center',
  30255. /**
  30256. * Whether to allow data labels to overlap. To make the labels less
  30257. * sensitive for overlapping, the
  30258. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  30259. * can be set to 0.
  30260. *
  30261. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  30262. * Don't allow overlap
  30263. *
  30264. * @type {boolean}
  30265. * @default false
  30266. * @since 4.1.0
  30267. * @apioption plotOptions.series.dataLabels.allowOverlap
  30268. */
  30269. /**
  30270. * The background color or gradient for the data label. Setting it to
  30271. * `auto` will use the point's color.
  30272. *
  30273. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30274. * Data labels box options
  30275. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30276. * Data labels box options
  30277. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30278. * Data labels as map markers
  30279. *
  30280. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30281. * @since 2.2.1
  30282. * @apioption plotOptions.series.dataLabels.backgroundColor
  30283. */
  30284. /**
  30285. * The border color for the data label. Setting it to `auto` will use
  30286. * the point's color. Defaults to `undefined`.
  30287. *
  30288. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30289. * Data labels box options
  30290. *
  30291. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30292. * @since 2.2.1
  30293. * @apioption plotOptions.series.dataLabels.borderColor
  30294. */
  30295. /**
  30296. * The border radius in pixels for the data label.
  30297. *
  30298. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30299. * Data labels box options
  30300. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30301. * Data labels box options
  30302. *
  30303. * @type {number}
  30304. * @default 0
  30305. * @since 2.2.1
  30306. * @apioption plotOptions.series.dataLabels.borderRadius
  30307. */
  30308. /**
  30309. * The border width in pixels for the data label.
  30310. *
  30311. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30312. * Data labels box options
  30313. *
  30314. * @type {number}
  30315. * @default 0
  30316. * @since 2.2.1
  30317. * @apioption plotOptions.series.dataLabels.borderWidth
  30318. */
  30319. borderWidth: 0,
  30320. /**
  30321. * A class name for the data label. Particularly in styled mode,
  30322. * this can be used to give each series' or point's data label
  30323. * unique styling. In addition to this option, a default color class
  30324. * name is added so that we can give the labels a contrast text
  30325. * shadow.
  30326. *
  30327. * @sample {highcharts} highcharts/css/data-label-contrast/
  30328. * Contrast text shadow
  30329. * @sample {highcharts} highcharts/css/series-datalabels/
  30330. * Styling by CSS
  30331. *
  30332. * @type {string}
  30333. * @since 5.0.0
  30334. * @apioption plotOptions.series.dataLabels.className
  30335. */
  30336. /**
  30337. * This options is deprecated.
  30338. * Use [style.color](#plotOptions.series.dataLabels.style) instead.
  30339. *
  30340. * The text color for the data labels. Defaults to `undefined`. For
  30341. * certain series types, like column or map, the data labels can be
  30342. * drawn inside the points. In this case the data label will be
  30343. * drawn with maximum contrast by default. Additionally, it will be
  30344. * given a `text-outline` style with the opposite color, to further
  30345. * increase the contrast. This can be overridden by setting the
  30346. * `text-outline` style to `none` in the `dataLabels.style` option.
  30347. *
  30348. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  30349. * Red data labels
  30350. * @sample {highmaps} maps/demo/color-axis/
  30351. * White data labels
  30352. *
  30353. * @see [style.color](#plotOptions.series.dataLabels.style)
  30354. *
  30355. * @type {Highcharts.ColorType}
  30356. * @deprecated 10.3
  30357. * @apioption plotOptions.series.dataLabels.color
  30358. */
  30359. /**
  30360. * Whether to hide data labels that are outside the plot area. By
  30361. * default, the data label is moved inside the plot area according
  30362. * to the
  30363. * [overflow](#plotOptions.series.dataLabels.overflow)
  30364. * option.
  30365. *
  30366. * @type {boolean}
  30367. * @default true
  30368. * @since 2.3.3
  30369. * @apioption plotOptions.series.dataLabels.crop
  30370. */
  30371. /**
  30372. * Whether to defer displaying the data labels until the initial
  30373. * series animation has finished. Setting to `false` renders the
  30374. * data label immediately. If set to `true` inherits the defer
  30375. * time set in [plotOptions.series.animation](#plotOptions.series.animation).
  30376. *
  30377. * @since 4.0.0
  30378. * @type {boolean}
  30379. * @product highcharts highstock gantt
  30380. */
  30381. defer: true,
  30382. /**
  30383. * Enable or disable the data labels.
  30384. *
  30385. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  30386. * Data labels enabled
  30387. * @sample {highmaps} maps/demo/color-axis/
  30388. * Data labels enabled
  30389. *
  30390. * @type {boolean}
  30391. * @default false
  30392. * @apioption plotOptions.series.dataLabels.enabled
  30393. */
  30394. /**
  30395. * A declarative filter to control of which data labels to display.
  30396. * The declarative filter is designed for use when callback
  30397. * functions are not available, like when the chart options require
  30398. * a pure JSON structure or for use with graphical editors. For
  30399. * programmatic control, use the `formatter` instead, and return
  30400. * `undefined` to disable a single data label.
  30401. *
  30402. * @example
  30403. * filter: {
  30404. * property: 'percentage',
  30405. * operator: '>',
  30406. * value: 4
  30407. * }
  30408. *
  30409. * @sample {highcharts} highcharts/demo/pie-monochrome
  30410. * Data labels filtered by percentage
  30411. *
  30412. * @declare Highcharts.DataLabelsFilterOptionsObject
  30413. * @since 6.0.3
  30414. * @apioption plotOptions.series.dataLabels.filter
  30415. */
  30416. /**
  30417. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  30418. * `==`, and `===`.
  30419. *
  30420. * @type {string}
  30421. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  30422. * @apioption plotOptions.series.dataLabels.filter.operator
  30423. */
  30424. /**
  30425. * The point property to filter by. Point options are passed
  30426. * directly to properties, additionally there are `y` value,
  30427. * `percentage` and others listed under {@link Highcharts.Point}
  30428. * members.
  30429. *
  30430. * @type {string}
  30431. * @apioption plotOptions.series.dataLabels.filter.property
  30432. */
  30433. /**
  30434. * The value to compare against.
  30435. *
  30436. * @type {number}
  30437. * @apioption plotOptions.series.dataLabels.filter.value
  30438. */
  30439. /**
  30440. * A
  30441. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  30442. * for the data label. Available variables are the same as for
  30443. * `formatter`.
  30444. *
  30445. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30446. * Add a unit
  30447. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format-subexpression/
  30448. * Complex logic in the format string
  30449. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30450. * Formatted value in the data label
  30451. *
  30452. * @type {string}
  30453. * @default y
  30454. * @default point.value
  30455. * @since 3.0
  30456. * @apioption plotOptions.series.dataLabels.format
  30457. */
  30458. // eslint-disable-next-line valid-jsdoc
  30459. /**
  30460. * Callback JavaScript function to format the data label. Note that if a
  30461. * `format` is defined, the format takes precedence and the formatter is
  30462. * ignored.
  30463. *
  30464. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30465. * Formatted value
  30466. *
  30467. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30468. */
  30469. formatter: function () {
  30470. const { numberFormatter } = this.series.chart;
  30471. return typeof this.y !== 'number' ?
  30472. '' : numberFormatter(this.y, -1);
  30473. },
  30474. /**
  30475. * For points with an extent, like columns or map areas, whether to
  30476. * align the data label inside the box or to the actual value point.
  30477. * Defaults to `false` in most cases, `true` in stacked columns.
  30478. *
  30479. * @type {boolean}
  30480. * @since 3.0
  30481. * @apioption plotOptions.series.dataLabels.inside
  30482. */
  30483. /**
  30484. * Format for points with the value of null. Works analogously to
  30485. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  30486. * be applied only to series which support displaying null points
  30487. * i.e `heatmap` or `tilemap`. Does not work with series that don't
  30488. * display null points, like `line`, `column`, `bar` or `pie`.
  30489. *
  30490. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30491. * Format data label for null points in heat map
  30492. *
  30493. * @type {boolean|string}
  30494. * @since 7.1.0
  30495. * @apioption plotOptions.series.dataLabels.nullFormat
  30496. */
  30497. /**
  30498. * Callback JavaScript function that defines formatting for points
  30499. * with the value of null. Works analogously to
  30500. * [formatter](#plotOptions.series.dataLabels.formatter).
  30501. * `nullFormatter` can be applied only to series which support
  30502. * displaying null points i.e `heatmap` or `tilemap`. Does not work
  30503. * with series that don't display null points, like `line`, `column`,
  30504. * `bar` or `pie`.
  30505. *
  30506. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30507. * Format data label for null points in heat map
  30508. *
  30509. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30510. * @since 7.1.0
  30511. * @apioption plotOptions.series.dataLabels.nullFormatter
  30512. */
  30513. /**
  30514. * How to handle data labels that flow outside the plot area. The
  30515. * default is `"justify"`, which aligns them inside the plot area.
  30516. * For columns and bars, this means it will be moved inside the bar.
  30517. * To display data labels outside the plot area, set `crop` to
  30518. * `false` and `overflow` to `"allow"`.
  30519. *
  30520. * @type {Highcharts.DataLabelsOverflowValue}
  30521. * @default justify
  30522. * @since 3.0.6
  30523. * @apioption plotOptions.series.dataLabels.overflow
  30524. */
  30525. /**
  30526. * When either the `borderWidth` or the `backgroundColor` is set,
  30527. * this is the padding within the box.
  30528. *
  30529. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30530. * Data labels box options
  30531. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30532. * Data labels box options
  30533. *
  30534. * @since 2.2.1
  30535. */
  30536. padding: 5,
  30537. /**
  30538. * Aligns data labels relative to points. If `center` alignment is
  30539. * not possible, it defaults to `right`.
  30540. *
  30541. * @type {Highcharts.AlignValue}
  30542. * @default center
  30543. * @apioption plotOptions.series.dataLabels.position
  30544. */
  30545. /**
  30546. * Text rotation in degrees. Note that due to a more complex
  30547. * structure, backgrounds, borders and padding will be lost on a
  30548. * rotated data label.
  30549. *
  30550. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30551. * Vertical labels
  30552. *
  30553. * @type {number}
  30554. * @default 0
  30555. * @apioption plotOptions.series.dataLabels.rotation
  30556. */
  30557. /**
  30558. * The shadow of the box. Works best with `borderWidth` or
  30559. * `backgroundColor`. Since 2.3 the shadow can be an object
  30560. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  30561. * and `width`.
  30562. *
  30563. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30564. * Data labels box options
  30565. *
  30566. * @type {boolean|Highcharts.ShadowOptionsObject}
  30567. * @default false
  30568. * @since 2.2.1
  30569. * @apioption plotOptions.series.dataLabels.shadow
  30570. */
  30571. /**
  30572. * The name of a symbol to use for the border around the label.
  30573. * Symbols are predefined functions on the Renderer object.
  30574. *
  30575. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  30576. * A callout for annotations
  30577. *
  30578. * @type {string}
  30579. * @default square
  30580. * @since 4.1.2
  30581. * @apioption plotOptions.series.dataLabels.shape
  30582. */
  30583. /**
  30584. * Styles for the label. The default `color` setting is
  30585. * `"contrast"`, which is a pseudo color that Highcharts picks up
  30586. * and applies the maximum contrast to the underlying point item,
  30587. * for example the bar in a bar chart.
  30588. *
  30589. * The `textOutline` is a pseudo property that applies an outline of
  30590. * the given width with the given color, which by default is the
  30591. * maximum contrast to the text. So a bright text color will result
  30592. * in a black text outline for maximum readability on a mixed
  30593. * background. In some cases, especially with grayscale text, the
  30594. * text outline doesn't work well, in which cases it can be disabled
  30595. * by setting it to `"none"`. When `useHTML` is true, the
  30596. * `textOutline` will not be picked up. In this, case, the same
  30597. * effect can be acheived through the `text-shadow` CSS property.
  30598. *
  30599. * For some series types, where each point has an extent, like for
  30600. * example tree maps, the data label may overflow the point. There
  30601. * are two strategies for handling overflow. By default, the text
  30602. * will wrap to multiple lines. The other strategy is to set
  30603. * `style.textOverflow` to `ellipsis`, which will keep the text on
  30604. * one line plus it will break inside long words.
  30605. *
  30606. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  30607. * Bold labels
  30608. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
  30609. * Long labels truncated with an ellipsis in a pie
  30610. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
  30611. * Long labels are wrapped in a pie
  30612. * @sample {highmaps} maps/demo/color-axis/
  30613. * Bold labels
  30614. *
  30615. * @type {Highcharts.CSSObject}
  30616. * @since 4.1.0
  30617. * @apioption plotOptions.series.dataLabels.style
  30618. */
  30619. style: {
  30620. /** @internal */
  30621. fontSize: '0.7em',
  30622. /** @internal */
  30623. fontWeight: 'bold',
  30624. /** @internal */
  30625. color: 'contrast',
  30626. /** @internal */
  30627. textOutline: '1px contrast'
  30628. },
  30629. /**
  30630. * Options for a label text which should follow marker's shape.
  30631. * Border and background are disabled for a label that follows a
  30632. * path.
  30633. *
  30634. * **Note:** Only SVG-based renderer supports this option. Setting
  30635. * `useHTML` to true will disable this option.
  30636. *
  30637. * @declare Highcharts.DataLabelsTextPathOptionsObject
  30638. * @since 7.1.0
  30639. * @apioption plotOptions.series.dataLabels.textPath
  30640. */
  30641. /**
  30642. * Presentation attributes for the text path.
  30643. *
  30644. * @type {Highcharts.SVGAttributes}
  30645. * @since 7.1.0
  30646. * @apioption plotOptions.series.dataLabels.textPath.attributes
  30647. */
  30648. /**
  30649. * Enable or disable `textPath` option for link's or marker's data
  30650. * labels.
  30651. *
  30652. * @type {boolean}
  30653. * @since 7.1.0
  30654. * @apioption plotOptions.series.dataLabels.textPath.enabled
  30655. */
  30656. /**
  30657. * Whether to
  30658. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  30659. * to render the labels.
  30660. *
  30661. * @type {boolean}
  30662. * @default false
  30663. * @apioption plotOptions.series.dataLabels.useHTML
  30664. */
  30665. /**
  30666. * The vertical alignment of a data label. Can be one of `top`,
  30667. * `middle` or `bottom`. The default value depends on the data, for
  30668. * instance in a column chart, the label is above positive values
  30669. * and below negative values.
  30670. *
  30671. * @type {Highcharts.VerticalAlignValue|null}
  30672. * @since 2.3.3
  30673. */
  30674. verticalAlign: 'bottom',
  30675. /**
  30676. * The x position offset of the label relative to the point in
  30677. * pixels.
  30678. *
  30679. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30680. * Vertical and positioned
  30681. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  30682. * Data labels inside the bar
  30683. */
  30684. x: 0,
  30685. /**
  30686. * The z index of the data labels. Use a `zIndex` of 6 to display it above
  30687. * the series, or use a `zIndex` of 2 to display it behind the series.
  30688. *
  30689. * @type {number}
  30690. * @default 6
  30691. * @since 2.3.5
  30692. * @apioption plotOptions.series.dataLabels.zIndex
  30693. */
  30694. /**
  30695. * The y position offset of the label relative to the point in
  30696. * pixels.
  30697. *
  30698. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30699. * Vertical and positioned
  30700. */
  30701. y: 0
  30702. },
  30703. /**
  30704. * When the series contains less points than the crop threshold, all
  30705. * points are drawn, even if the points fall outside the visible plot
  30706. * area at the current zoom. The advantage of drawing all points
  30707. * (including markers and columns), is that animation is performed on
  30708. * updates. On the other hand, when the series contains more points than
  30709. * the crop threshold, the series data is cropped to only contain points
  30710. * that fall within the plot area. The advantage of cropping away
  30711. * invisible points is to increase performance on large series.
  30712. *
  30713. * @since 2.2
  30714. * @product highcharts highstock
  30715. *
  30716. * @private
  30717. */
  30718. cropThreshold: 300,
  30719. /**
  30720. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  30721. *
  30722. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  30723. *
  30724. * @since 7.1.0
  30725. *
  30726. * @private
  30727. */
  30728. opacity: 1,
  30729. /**
  30730. * The width of each point on the x axis. For example in a column chart
  30731. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  30732. * * 1000 milliseconds). This is normally computed automatically, but
  30733. * this option can be used to override the automatic value.
  30734. *
  30735. * @product highstock
  30736. *
  30737. * @private
  30738. */
  30739. pointRange: 0,
  30740. /**
  30741. * When this is true, the series will not cause the Y axis to cross
  30742. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  30743. * unless the data actually crosses the plane.
  30744. *
  30745. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  30746. * 3 will make the Y axis show negative values according to the
  30747. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  30748. * at 0.
  30749. *
  30750. * @since 4.1.9
  30751. * @product highcharts highstock
  30752. *
  30753. * @private
  30754. */
  30755. softThreshold: true,
  30756. /**
  30757. * @declare Highcharts.SeriesStatesOptionsObject
  30758. *
  30759. * @private
  30760. */
  30761. states: {
  30762. /**
  30763. * The normal state of a series, or for point items in column, pie
  30764. * and similar series. Currently only used for setting animation
  30765. * when returning to normal state from hover.
  30766. *
  30767. * @declare Highcharts.SeriesStatesNormalOptionsObject
  30768. */
  30769. normal: {
  30770. /**
  30771. * Animation when returning to normal state after hovering.
  30772. *
  30773. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30774. */
  30775. animation: true
  30776. },
  30777. /**
  30778. * Options for the hovered series. These settings override the
  30779. * normal state options when a series is moused over or touched.
  30780. *
  30781. * @declare Highcharts.SeriesStatesHoverOptionsObject
  30782. */
  30783. hover: {
  30784. /**
  30785. * Enable separate styles for the hovered series to visualize
  30786. * that the user hovers either the series itself or the legend.
  30787. *
  30788. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  30789. * Line
  30790. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  30791. * Column
  30792. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  30793. * Pie
  30794. *
  30795. * @type {boolean}
  30796. * @default true
  30797. * @since 1.2
  30798. * @apioption plotOptions.series.states.hover.enabled
  30799. */
  30800. /**
  30801. * Animation setting for hovering the graph in line-type series.
  30802. *
  30803. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30804. * @since 5.0.8
  30805. * @product highcharts highstock
  30806. */
  30807. animation: {
  30808. /**
  30809. * The duration of the hover animation in milliseconds. By
  30810. * default the hover state animates quickly in, and slowly
  30811. * back to normal.
  30812. *
  30813. * @internal
  30814. */
  30815. duration: 150
  30816. },
  30817. /**
  30818. * Pixel width of the graph line. By default this property is
  30819. * undefined, and the `lineWidthPlus` property dictates how much
  30820. * to increase the linewidth from normal state.
  30821. *
  30822. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  30823. * 5px line on hover
  30824. *
  30825. * @type {number}
  30826. * @product highcharts highstock
  30827. * @apioption plotOptions.series.states.hover.lineWidth
  30828. */
  30829. /**
  30830. * The additional line width for the graph of a hovered series.
  30831. *
  30832. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  30833. * 5 pixels wider
  30834. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  30835. * 5 pixels wider
  30836. *
  30837. * @since 4.0.3
  30838. * @product highcharts highstock
  30839. */
  30840. lineWidthPlus: 1,
  30841. /**
  30842. * In Highcharts 1.0, the appearance of all markers belonging
  30843. * to the hovered series. For settings on the hover state of the
  30844. * individual point, see
  30845. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  30846. *
  30847. * @deprecated
  30848. *
  30849. * @extends plotOptions.series.marker
  30850. * @excluding states, symbol
  30851. * @product highcharts highstock
  30852. */
  30853. marker: {
  30854. // lineWidth: base + 1,
  30855. // radius: base + 1
  30856. },
  30857. /**
  30858. * Options for the halo appearing around the hovered point in
  30859. * line-type series as well as outside the hovered slice in pie
  30860. * charts. By default the halo is filled by the current point or
  30861. * series color with an opacity of 0.25\. The halo can be
  30862. * disabled by setting the `halo` option to `null`.
  30863. *
  30864. * In styled mode, the halo is styled with the
  30865. * `.highcharts-halo` class, with colors inherited from
  30866. * `.highcharts-color-{n}`.
  30867. *
  30868. * @sample {highcharts} highcharts/plotoptions/halo/
  30869. * Halo options
  30870. * @sample {highstock} highcharts/plotoptions/halo/
  30871. * Halo options
  30872. *
  30873. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  30874. * @type {null|*}
  30875. * @since 4.0
  30876. * @product highcharts highstock
  30877. */
  30878. halo: {
  30879. /**
  30880. * A collection of SVG attributes to override the appearance
  30881. * of the halo, for example `fill`, `stroke` and
  30882. * `stroke-width`.
  30883. *
  30884. * @type {Highcharts.SVGAttributes}
  30885. * @since 4.0
  30886. * @product highcharts highstock
  30887. * @apioption plotOptions.series.states.hover.halo.attributes
  30888. */
  30889. /**
  30890. * The pixel size of the halo. For point markers this is the
  30891. * radius of the halo. For pie slices it is the width of the
  30892. * halo outside the slice. For bubbles it defaults to 5 and
  30893. * is the width of the halo outside the bubble.
  30894. *
  30895. * @since 4.0
  30896. * @product highcharts highstock
  30897. */
  30898. size: 10,
  30899. /**
  30900. * Opacity for the halo unless a specific fill is overridden
  30901. * using the `attributes` setting. Note that Highcharts is
  30902. * only able to apply opacity to colors of hex or rgb(a)
  30903. * formats.
  30904. *
  30905. * @since 4.0
  30906. * @product highcharts highstock
  30907. */
  30908. opacity: 0.25
  30909. }
  30910. },
  30911. /**
  30912. * Specific options for point in selected states, after being
  30913. * selected by
  30914. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  30915. * or programmatically.
  30916. *
  30917. * @sample maps/plotoptions/series-allowpointselect/
  30918. * Allow point select demo
  30919. *
  30920. * @declare Highcharts.SeriesStatesSelectOptionsObject
  30921. * @extends plotOptions.series.states.hover
  30922. * @excluding brightness
  30923. */
  30924. select: {
  30925. animation: {
  30926. /** @internal */
  30927. duration: 0
  30928. }
  30929. },
  30930. /**
  30931. * The opposite state of a hover for series.
  30932. *
  30933. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30934. * Disabled inactive state
  30935. *
  30936. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  30937. */
  30938. inactive: {
  30939. /**
  30940. * Enable or disable the inactive state for a series
  30941. *
  30942. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30943. * Disabled inactive state
  30944. *
  30945. * @type {boolean}
  30946. * @default true
  30947. * @apioption plotOptions.series.states.inactive.enabled
  30948. */
  30949. /**
  30950. * The animation for entering the inactive state.
  30951. *
  30952. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30953. */
  30954. animation: {
  30955. /** @internal */
  30956. duration: 150
  30957. },
  30958. /**
  30959. * Opacity of series elements (dataLabels, line, area).
  30960. *
  30961. * @type {number}
  30962. */
  30963. opacity: 0.2
  30964. }
  30965. },
  30966. /**
  30967. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  30968. * series isn't triggered until the mouse moves over another series, or
  30969. * out of the plot area. When false, the `mouseOut` event on a series is
  30970. * triggered when the mouse leaves the area around the series' graph or
  30971. * markers. This also implies the tooltip when not shared. When
  30972. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  30973. * will be hidden when moving the mouse between series. Defaults to true
  30974. * for line and area type series, but to false for columns, pies etc.
  30975. *
  30976. * **Note:** The boost module will force this option because of
  30977. * technical limitations.
  30978. *
  30979. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  30980. * True by default
  30981. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  30982. * False
  30983. *
  30984. * @default {highcharts} true
  30985. * @default {highstock} true
  30986. * @default {highmaps} false
  30987. * @since 2.0
  30988. *
  30989. * @private
  30990. */
  30991. stickyTracking: true,
  30992. /**
  30993. * A configuration object for the tooltip rendering of each single
  30994. * series. Properties are inherited from [tooltip](#tooltip), but only
  30995. * the following properties can be defined on a series level.
  30996. *
  30997. * @declare Highcharts.SeriesTooltipOptionsObject
  30998. * @since 2.3
  30999. * @extends tooltip
  31000. * @excluding animation, backgroundColor, borderColor, borderRadius,
  31001. * borderWidth, className, crosshairs, enabled, formatter,
  31002. * headerShape, hideDelay, outside, padding, positioner,
  31003. * shadow, shape, shared, snap, split, stickOnContact,
  31004. * style, useHTML
  31005. * @apioption plotOptions.series.tooltip
  31006. */
  31007. /**
  31008. * When a series contains a data array that is longer than this, only
  31009. * one dimensional arrays of numbers, or two dimensional arrays with
  31010. * x and y values are allowed. Also, only the first point is tested,
  31011. * and the rest are assumed to be the same format. This saves expensive
  31012. * data checking and indexing in long series. Set it to `0` disable.
  31013. *
  31014. * Note:
  31015. * In boost mode turbo threshold is forced. Only array of numbers or
  31016. * two dimensional arrays are allowed.
  31017. *
  31018. * @since 2.2
  31019. * @product highcharts highstock gantt
  31020. *
  31021. * @private
  31022. */
  31023. turboThreshold: 1000,
  31024. /**
  31025. * An array defining zones within a series. Zones can be applied to the
  31026. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  31027. * option. The zone definitions have to be in ascending order regarding
  31028. * to the value.
  31029. *
  31030. * In styled mode, the color zones are styled with the
  31031. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  31032. * option
  31033. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  31034. *
  31035. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  31036. *
  31037. * @sample {highcharts} highcharts/series/color-zones-simple/
  31038. * Color zones
  31039. * @sample {highstock} highcharts/series/color-zones-simple/
  31040. * Color zones
  31041. *
  31042. * @declare Highcharts.SeriesZonesOptionsObject
  31043. * @type {Array<*>}
  31044. * @since 4.1.0
  31045. * @product highcharts highstock
  31046. * @apioption plotOptions.series.zones
  31047. */
  31048. /**
  31049. * Styled mode only. A custom class name for the zone.
  31050. *
  31051. * @sample highcharts/css/color-zones/
  31052. * Zones styled by class name
  31053. *
  31054. * @type {string}
  31055. * @since 5.0.0
  31056. * @apioption plotOptions.series.zones.className
  31057. */
  31058. /**
  31059. * Defines the color of the series.
  31060. *
  31061. * @see [series color](#plotOptions.series.color)
  31062. *
  31063. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31064. * @since 4.1.0
  31065. * @product highcharts highstock
  31066. * @apioption plotOptions.series.zones.color
  31067. */
  31068. /**
  31069. * A name for the dash style to use for the graph.
  31070. *
  31071. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  31072. *
  31073. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  31074. * Dashed line indicates prognosis
  31075. *
  31076. * @type {Highcharts.DashStyleValue}
  31077. * @since 4.1.0
  31078. * @product highcharts highstock
  31079. * @apioption plotOptions.series.zones.dashStyle
  31080. */
  31081. /**
  31082. * Defines the fill color for the series (in area type series)
  31083. *
  31084. * @see [fillColor](#plotOptions.area.fillColor)
  31085. *
  31086. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31087. * @since 4.1.0
  31088. * @product highcharts highstock
  31089. * @apioption plotOptions.series.zones.fillColor
  31090. */
  31091. /**
  31092. * The value up to where the zone extends, if undefined the zones
  31093. * stretches to the last value in the series.
  31094. *
  31095. * @type {number}
  31096. * @since 4.1.0
  31097. * @product highcharts highstock
  31098. * @apioption plotOptions.series.zones.value
  31099. */
  31100. /**
  31101. * When using dual or multiple color axes, this number defines which
  31102. * colorAxis the particular series is connected to. It refers to
  31103. * either the
  31104. * {@link #colorAxis.id|axis id}
  31105. * or the index of the axis in the colorAxis array, with 0 being the
  31106. * first. Set this option to false to prevent a series from connecting
  31107. * to the default color axis.
  31108. *
  31109. * Since v7.2.0 the option can also be an axis id or an axis index
  31110. * instead of a boolean flag.
  31111. *
  31112. * @sample highcharts/coloraxis/coloraxis-with-pie/
  31113. * Color axis with pie series
  31114. * @sample highcharts/coloraxis/multiple-coloraxis/
  31115. * Multiple color axis
  31116. *
  31117. * @type {number|string|boolean}
  31118. * @default 0
  31119. * @product highcharts highstock highmaps
  31120. * @apioption plotOptions.series.colorAxis
  31121. */
  31122. /**
  31123. * Determines what data value should be used to calculate point color
  31124. * if `colorAxis` is used. Requires to set `min` and `max` if some
  31125. * custom point property is used or if approximation for data grouping
  31126. * is set to `'sum'`.
  31127. *
  31128. * @sample highcharts/coloraxis/custom-color-key/
  31129. * Custom color key
  31130. * @sample highcharts/coloraxis/color-key-with-stops/
  31131. * Custom colorKey with color axis stops
  31132. * @sample highcharts/coloraxis/changed-default-color-key/
  31133. * Changed default color key
  31134. *
  31135. * @type {string}
  31136. * @default y
  31137. * @since 7.2.0
  31138. * @product highcharts highstock highmaps
  31139. * @apioption plotOptions.series.colorKey
  31140. */
  31141. /**
  31142. * What type of legend symbol to render for this series. Can be one of
  31143. * `lineMarker` or `rectangle`.
  31144. *
  31145. * @validvalue ["lineMarker", "rectangle"]
  31146. *
  31147. * @sample {highcharts} highcharts/series/legend-symbol/
  31148. * Change the legend symbol
  31149. *
  31150. * @type {string}
  31151. * @default rectangle
  31152. * @since 11.0.1
  31153. * @apioption plotOptions.series.legendSymbol
  31154. */
  31155. /**
  31156. * Determines whether the series should look for the nearest point
  31157. * in both dimensions or just the x-dimension when hovering the series.
  31158. * Defaults to `'xy'` for scatter series and `'x'` for most other
  31159. * series. If the data has duplicate x-values, it is recommended to
  31160. * set this to `'xy'` to allow hovering over all points.
  31161. *
  31162. * Applies only to series types using nearest neighbor search (not
  31163. * direct hover) for tooltip.
  31164. *
  31165. * @sample {highcharts} highcharts/series/findnearestpointby/
  31166. * Different hover behaviors
  31167. * @sample {highstock} highcharts/series/findnearestpointby/
  31168. * Different hover behaviors
  31169. * @sample {highmaps} highcharts/series/findnearestpointby/
  31170. * Different hover behaviors
  31171. *
  31172. * @since 5.0.10
  31173. * @validvalue ["x", "xy"]
  31174. *
  31175. * @private
  31176. */
  31177. findNearestPointBy: 'x'
  31178. };
  31179. /* *
  31180. *
  31181. * Default Export
  31182. *
  31183. * */
  31184. return seriesDefaults;
  31185. });
  31186. _registerModule(_modules, 'Core/Series/SeriesRegistry.js', [_modules['Core/Globals.js'], _modules['Core/Defaults.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, D, Point, U) {
  31187. /* *
  31188. *
  31189. * (c) 2010-2021 Torstein Honsi
  31190. *
  31191. * License: www.highcharts.com/license
  31192. *
  31193. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31194. *
  31195. * */
  31196. const { defaultOptions } = D;
  31197. const { extendClass, merge } = U;
  31198. /* *
  31199. *
  31200. * Namespace
  31201. *
  31202. * */
  31203. var SeriesRegistry;
  31204. (function (SeriesRegistry) {
  31205. /* *
  31206. *
  31207. * Properties
  31208. *
  31209. * */
  31210. /**
  31211. * @internal
  31212. * @todo Move `Globals.seriesTypes` code to her.
  31213. */
  31214. SeriesRegistry.seriesTypes = H.seriesTypes;
  31215. /* *
  31216. *
  31217. * Functions
  31218. *
  31219. * */
  31220. /**
  31221. * Registers class pattern of a series.
  31222. *
  31223. * @private
  31224. */
  31225. function registerSeriesType(seriesType, SeriesClass) {
  31226. const defaultPlotOptions = defaultOptions.plotOptions || {}, seriesOptions = SeriesClass.defaultOptions, seriesProto = SeriesClass.prototype;
  31227. seriesProto.type = seriesType;
  31228. if (!seriesProto.pointClass) {
  31229. seriesProto.pointClass = Point;
  31230. }
  31231. if (seriesOptions) {
  31232. defaultPlotOptions[seriesType] = seriesOptions;
  31233. }
  31234. SeriesRegistry.seriesTypes[seriesType] = SeriesClass;
  31235. }
  31236. SeriesRegistry.registerSeriesType = registerSeriesType;
  31237. /**
  31238. * Old factory to create new series prototypes.
  31239. *
  31240. * @deprecated
  31241. * @function Highcharts.seriesType
  31242. *
  31243. * @param {string} type
  31244. * The series type name.
  31245. *
  31246. * @param {string} parent
  31247. * The parent series type name. Use `line` to inherit from the basic
  31248. * {@link Series} object.
  31249. *
  31250. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  31251. * The additional default options that are merged with the parent's options.
  31252. *
  31253. * @param {Highcharts.Dictionary<*>} [props]
  31254. * The properties (functions and primitives) to set on the new prototype.
  31255. *
  31256. * @param {Highcharts.Dictionary<*>} [pointProps]
  31257. * Members for a series-specific extension of the {@link Point} prototype if
  31258. * needed.
  31259. *
  31260. * @return {Highcharts.Series}
  31261. * The newly created prototype as extended from {@link Series} or its
  31262. * derivatives.
  31263. */
  31264. function seriesType(type, parent, options, seriesProto, pointProto) {
  31265. const defaultPlotOptions = defaultOptions.plotOptions || {};
  31266. parent = parent || '';
  31267. // Merge the options
  31268. defaultPlotOptions[type] = merge(defaultPlotOptions[parent], options);
  31269. // Create the class
  31270. registerSeriesType(type, extendClass(SeriesRegistry.seriesTypes[parent] || function () { }, seriesProto));
  31271. SeriesRegistry.seriesTypes[type].prototype.type = type;
  31272. // Create the point class if needed
  31273. if (pointProto) {
  31274. SeriesRegistry.seriesTypes[type].prototype.pointClass = extendClass(Point, pointProto);
  31275. }
  31276. return SeriesRegistry.seriesTypes[type];
  31277. }
  31278. SeriesRegistry.seriesType = seriesType;
  31279. })(SeriesRegistry || (SeriesRegistry = {}));
  31280. /* *
  31281. *
  31282. * Default Export
  31283. *
  31284. * */
  31285. return SeriesRegistry;
  31286. });
  31287. _registerModule(_modules, 'Core/Series/Series.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Defaults.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Legend/LegendSymbol.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/SeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (A, D, F, H, LegendSymbol, Point, SeriesDefaults, SeriesRegistry, SVGElement, U) {
  31288. /* *
  31289. *
  31290. * (c) 2010-2021 Torstein Honsi
  31291. *
  31292. * License: www.highcharts.com/license
  31293. *
  31294. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31295. *
  31296. * */
  31297. const { animObject, setAnimation } = A;
  31298. const { defaultOptions } = D;
  31299. const { registerEventOptions } = F;
  31300. const { hasTouch, svg, win } = H;
  31301. const { seriesTypes } = SeriesRegistry;
  31302. const { addEvent, arrayMax, arrayMin, clamp, correctFloat, defined, diffObjects, erase, error, extend, find, fireEvent, getClosestDistance, getNestedProperty, insertItem, isArray, isNumber, isString, merge, objectEach, pick, removeEvent, splat, syncTimeout } = U;
  31303. /* *
  31304. *
  31305. * Class
  31306. *
  31307. * */
  31308. /**
  31309. * This is the base series prototype that all other series types inherit from.
  31310. * A new series is initialized either through the
  31311. * [series](https://api.highcharts.com/highcharts/series)
  31312. * option structure, or after the chart is initialized, through
  31313. * {@link Highcharts.Chart#addSeries}.
  31314. *
  31315. * The object can be accessed in a number of ways. All series and point event
  31316. * handlers give a reference to the `series` object. The chart object has a
  31317. * {@link Highcharts.Chart#series|series} property that is a collection of all
  31318. * the chart's series. The point objects and axis objects also have the same
  31319. * reference.
  31320. *
  31321. * Another way to reference the series programmatically is by `id`. Add an id
  31322. * in the series configuration options, and get the series object by
  31323. * {@link Highcharts.Chart#get}.
  31324. *
  31325. * Configuration options for the series are given in three levels. Options for
  31326. * all series in a chart are given in the
  31327. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  31328. * object. Then options for all series of a specific type
  31329. * are given in the plotOptions of that type, for example `plotOptions.line`.
  31330. * Next, options for one single series are given in the series array, or as
  31331. * arguments to `chart.addSeries`.
  31332. *
  31333. * The data in the series is stored in various arrays.
  31334. *
  31335. * - First, `series.options.data` contains all the original config options for
  31336. * each point whether added by options or methods like `series.addPoint`.
  31337. *
  31338. * - Next, `series.data` contains those values converted to points, but in case
  31339. * the series data length exceeds the `cropThreshold`, or if the data is
  31340. * grouped, `series.data` doesn't contain all the points. It only contains the
  31341. * points that have been created on demand.
  31342. *
  31343. * - Then there's `series.points` that contains all currently visible point
  31344. * objects. In case of cropping, the cropped-away points are not part of this
  31345. * array. The `series.points` array starts at `series.cropStart` compared to
  31346. * `series.data` and `series.options.data`. If however the series data is
  31347. * grouped, these can't be correlated one to one.
  31348. *
  31349. * - `series.xData` and `series.processedXData` contain clean x values,
  31350. * equivalent to `series.data` and `series.points`.
  31351. *
  31352. * - `series.yData` and `series.processedYData` contain clean y values,
  31353. * equivalent to `series.data` and `series.points`.
  31354. *
  31355. * @class
  31356. * @name Highcharts.Series
  31357. *
  31358. * @param {Highcharts.Chart} chart
  31359. * The chart instance.
  31360. *
  31361. * @param {Highcharts.SeriesOptionsType|object} options
  31362. * The series options.
  31363. */
  31364. class Series {
  31365. constructor() {
  31366. /* *
  31367. *
  31368. * Static Properties
  31369. *
  31370. * */
  31371. this._i = void 0;
  31372. this.chart = void 0;
  31373. this.data = void 0;
  31374. this.eventOptions = void 0;
  31375. this.eventsToUnbind = void 0;
  31376. this.index = void 0;
  31377. this.linkedSeries = void 0;
  31378. this.options = void 0;
  31379. this.points = void 0;
  31380. this.processedXData = void 0;
  31381. this.processedYData = void 0;
  31382. this.tooltipOptions = void 0;
  31383. this.userOptions = void 0;
  31384. this.xAxis = void 0;
  31385. this.yAxis = void 0;
  31386. this.zones = void 0;
  31387. /** eslint-enable valid-jsdoc */
  31388. }
  31389. /* *
  31390. *
  31391. * Functions
  31392. *
  31393. * */
  31394. /* eslint-disable valid-jsdoc */
  31395. init(chart, userOptions) {
  31396. fireEvent(this, 'init', { options: userOptions });
  31397. const series = this, chartSeries = chart.series;
  31398. // The 'eventsToUnbind' property moved from prototype into the
  31399. // Series init to avoid reference to the same array between
  31400. // the different series and charts. #12959, #13937
  31401. this.eventsToUnbind = [];
  31402. /**
  31403. * Read only. The chart that the series belongs to.
  31404. *
  31405. * @name Highcharts.Series#chart
  31406. * @type {Highcharts.Chart}
  31407. */
  31408. series.chart = chart;
  31409. /**
  31410. * Read only. The series' type, like "line", "area", "column" etc.
  31411. * The type in the series options anc can be altered using
  31412. * {@link Series#update}.
  31413. *
  31414. * @name Highcharts.Series#type
  31415. * @type {string}
  31416. */
  31417. /**
  31418. * Read only. The series' current options. To update, use
  31419. * {@link Series#update}.
  31420. *
  31421. * @name Highcharts.Series#options
  31422. * @type {Highcharts.SeriesOptionsType}
  31423. */
  31424. series.options = series.setOptions(userOptions);
  31425. const options = series.options;
  31426. series.linkedSeries = [];
  31427. // bind the axes
  31428. series.bindAxes();
  31429. extend(series, {
  31430. /**
  31431. * The series name as given in the options. Defaults to
  31432. * "Series {n}".
  31433. *
  31434. * @name Highcharts.Series#name
  31435. * @type {string}
  31436. */
  31437. name: options.name,
  31438. state: '',
  31439. /**
  31440. * Read only. The series' visibility state as set by {@link
  31441. * Series#show}, {@link Series#hide}, or in the initial
  31442. * configuration.
  31443. *
  31444. * @name Highcharts.Series#visible
  31445. * @type {boolean}
  31446. */
  31447. visible: options.visible !== false,
  31448. /**
  31449. * Read only. The series' selected state as set by {@link
  31450. * Highcharts.Series#select}.
  31451. *
  31452. * @name Highcharts.Series#selected
  31453. * @type {boolean}
  31454. */
  31455. selected: options.selected === true // false by default
  31456. });
  31457. registerEventOptions(this, options);
  31458. const events = options.events;
  31459. if ((events && events.click) ||
  31460. (options.point &&
  31461. options.point.events &&
  31462. options.point.events.click) ||
  31463. options.allowPointSelect) {
  31464. chart.runTrackerClick = true;
  31465. }
  31466. series.getColor();
  31467. series.getSymbol();
  31468. // Initialize the parallel data arrays
  31469. series.parallelArrays.forEach(function (key) {
  31470. if (!series[key + 'Data']) {
  31471. series[key + 'Data'] = [];
  31472. }
  31473. });
  31474. // Mark cartesian
  31475. if (series.isCartesian) {
  31476. chart.hasCartesianSeries = true;
  31477. }
  31478. // Get the index and register the series in the chart. The index is
  31479. // one more than the current latest series index (#5960).
  31480. let lastSeries;
  31481. if (chartSeries.length) {
  31482. lastSeries = chartSeries[chartSeries.length - 1];
  31483. }
  31484. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  31485. series.opacity = series.options.opacity;
  31486. // Insert the series and re-order all series above the insertion
  31487. // point.
  31488. chart.orderItems('series', insertItem(this, chartSeries));
  31489. // Set options for series with sorting and set data later.
  31490. if (options.dataSorting && options.dataSorting.enabled) {
  31491. series.setDataSortingOptions();
  31492. }
  31493. else if (!series.points && !series.data) {
  31494. series.setData(options.data, false);
  31495. }
  31496. fireEvent(this, 'afterInit');
  31497. }
  31498. /**
  31499. * Check whether the series item is itself or inherits from a certain
  31500. * series type.
  31501. *
  31502. * @function Highcharts.Series#is
  31503. * @param {string} type The type of series to check for, can be either
  31504. * featured or custom series types. For example `column`, `pie`,
  31505. * `ohlc` etc.
  31506. *
  31507. * @return {boolean}
  31508. * True if this item is or inherits from the given type.
  31509. */
  31510. is(type) {
  31511. return seriesTypes[type] && this instanceof seriesTypes[type];
  31512. }
  31513. /**
  31514. * Set the xAxis and yAxis properties of cartesian series, and register
  31515. * the series in the `axis.series` array.
  31516. *
  31517. * @private
  31518. * @function Highcharts.Series#bindAxes
  31519. */
  31520. bindAxes() {
  31521. const series = this, seriesOptions = series.options, chart = series.chart;
  31522. let axisOptions;
  31523. fireEvent(this, 'bindAxes', null, function () {
  31524. // repeat for xAxis and yAxis
  31525. (series.axisTypes || []).forEach(function (coll) {
  31526. // loop through the chart's axis objects
  31527. chart[coll].forEach(function (axis) {
  31528. axisOptions = axis.options;
  31529. // apply if the series xAxis or yAxis option mathches
  31530. // the number of the axis, or if undefined, use the
  31531. // first axis
  31532. if (pick(seriesOptions[coll], 0) === axis.index ||
  31533. (typeof seriesOptions[coll] !==
  31534. 'undefined' &&
  31535. seriesOptions[coll] === axisOptions.id)) {
  31536. // register this series in the axis.series lookup
  31537. insertItem(series, axis.series);
  31538. // set this series.xAxis or series.yAxis reference
  31539. /**
  31540. * Read only. The unique xAxis object associated
  31541. * with the series.
  31542. *
  31543. * @name Highcharts.Series#xAxis
  31544. * @type {Highcharts.Axis}
  31545. */
  31546. /**
  31547. * Read only. The unique yAxis object associated
  31548. * with the series.
  31549. *
  31550. * @name Highcharts.Series#yAxis
  31551. * @type {Highcharts.Axis}
  31552. */
  31553. series[coll] = axis;
  31554. // mark dirty for redraw
  31555. axis.isDirty = true;
  31556. }
  31557. });
  31558. // The series needs an X and an Y axis
  31559. if (!series[coll] &&
  31560. series.optionalAxis !== coll) {
  31561. error(18, true, chart);
  31562. }
  31563. });
  31564. });
  31565. fireEvent(this, 'afterBindAxes');
  31566. }
  31567. /**
  31568. * For simple series types like line and column, the data values are
  31569. * held in arrays like xData and yData for quick lookup to find extremes
  31570. * and more. For multidimensional series like bubble and map, this can
  31571. * be extended with arrays like zData and valueData by adding to the
  31572. * `series.parallelArrays` array.
  31573. *
  31574. * @private
  31575. * @function Highcharts.Series#updateParallelArrays
  31576. */
  31577. updateParallelArrays(point, i, iArgs) {
  31578. const series = point.series, fn = isNumber(i) ?
  31579. // Insert the value in the given position
  31580. function (key) {
  31581. const val = key === 'y' && series.toYData ?
  31582. series.toYData(point) :
  31583. point[key];
  31584. series[key + 'Data'][i] = val;
  31585. } :
  31586. // Apply the method specified in i with the following
  31587. // arguments as arguments
  31588. function (key) {
  31589. Array.prototype[i].apply(series[key + 'Data'], iArgs);
  31590. };
  31591. series.parallelArrays.forEach(fn);
  31592. }
  31593. /**
  31594. * Define hasData functions for series. These return true if there
  31595. * are data points on this series within the plot area.
  31596. *
  31597. * @private
  31598. * @function Highcharts.Series#hasData
  31599. */
  31600. hasData() {
  31601. return ((this.visible &&
  31602. typeof this.dataMax !== 'undefined' &&
  31603. typeof this.dataMin !== 'undefined') || ( // #3703
  31604. this.visible &&
  31605. this.yData &&
  31606. this.yData.length > 0) // #9758
  31607. );
  31608. }
  31609. /**
  31610. * Return an auto incremented x value based on the pointStart and
  31611. * pointInterval options. This is only used if an x value is not given
  31612. * for the point that calls autoIncrement.
  31613. *
  31614. * @private
  31615. * @function Highcharts.Series#autoIncrement
  31616. */
  31617. autoIncrement(x) {
  31618. const options = this.options, pointIntervalUnit = options.pointIntervalUnit, relativeXValue = options.relativeXValue, time = this.chart.time;
  31619. let xIncrement = this.xIncrement, date, pointInterval;
  31620. xIncrement = pick(xIncrement, options.pointStart, 0);
  31621. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  31622. if (relativeXValue && isNumber(x)) {
  31623. pointInterval *= x;
  31624. }
  31625. // Added code for pointInterval strings
  31626. if (pointIntervalUnit) {
  31627. date = new time.Date(xIncrement);
  31628. if (pointIntervalUnit === 'day') {
  31629. time.set('Date', date, time.get('Date', date) + pointInterval);
  31630. }
  31631. else if (pointIntervalUnit === 'month') {
  31632. time.set('Month', date, time.get('Month', date) + pointInterval);
  31633. }
  31634. else if (pointIntervalUnit === 'year') {
  31635. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  31636. }
  31637. pointInterval = date.getTime() - xIncrement;
  31638. }
  31639. if (relativeXValue && isNumber(x)) {
  31640. return xIncrement + pointInterval;
  31641. }
  31642. this.xIncrement = xIncrement + pointInterval;
  31643. return xIncrement;
  31644. }
  31645. /**
  31646. * Internal function to set properties for series if data sorting is
  31647. * enabled.
  31648. *
  31649. * @private
  31650. * @function Highcharts.Series#setDataSortingOptions
  31651. */
  31652. setDataSortingOptions() {
  31653. const options = this.options;
  31654. extend(this, {
  31655. requireSorting: false,
  31656. sorted: false,
  31657. enabledDataSorting: true,
  31658. allowDG: false
  31659. });
  31660. // To allow unsorted data for column series.
  31661. if (!defined(options.pointRange)) {
  31662. options.pointRange = 1;
  31663. }
  31664. }
  31665. /**
  31666. * Set the series options by merging from the options tree. Called
  31667. * internally on initializing and updating series. This function will
  31668. * not redraw the series. For API usage, use {@link Series#update}.
  31669. * @private
  31670. * @function Highcharts.Series#setOptions
  31671. * @param {Highcharts.SeriesOptionsType} itemOptions
  31672. * The series options.
  31673. * @emits Highcharts.Series#event:afterSetOptions
  31674. */
  31675. setOptions(itemOptions) {
  31676. var _a,
  31677. _b;
  31678. const chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), styledMode = chart.styledMode, e = {
  31679. plotOptions: plotOptions,
  31680. userOptions: seriesUserOptions
  31681. };
  31682. let zone;
  31683. fireEvent(this, 'setOptions', e);
  31684. // These may be modified by the event
  31685. const typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {}), userPlotOptionsSeries = userPlotOptions.series || {}, defaultPlotOptionsType = (defaultOptions.plotOptions[this.type] || {}), userPlotOptionsType = userPlotOptions[this.type] || {};
  31686. // use copy to prevent undetected changes (#9762)
  31687. /**
  31688. * Contains series options by the user without defaults.
  31689. * @name Highcharts.Series#userOptions
  31690. * @type {Highcharts.SeriesOptionsType}
  31691. */
  31692. this.userOptions = e.userOptions;
  31693. const options = merge(typeOptions, plotOptions.series,
  31694. // #3881, chart instance plotOptions[type] should trump
  31695. // plotOptions.series
  31696. userPlotOptionsType, seriesUserOptions);
  31697. // The tooltip options are merged between global and series specific
  31698. // options. Importance order asscendingly:
  31699. // globals: (1)tooltip, (2)plotOptions.series,
  31700. // (3)plotOptions[this.type]
  31701. // init userOptions with possible later updates: 4-6 like 1-3 and
  31702. // (7)this series options
  31703. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  31704. (_a = defaultOptions.plotOptions.series) === null || _a === void 0 ? void 0 : _a.tooltip, // 2
  31705. defaultPlotOptionsType === null || defaultPlotOptionsType === void 0 ? void 0 : defaultPlotOptionsType.tooltip, // 3
  31706. chart.userOptions.tooltip, // 4
  31707. (_b = userPlotOptions.series) === null || _b === void 0 ? void 0 : _b.tooltip, // 5
  31708. userPlotOptionsType.tooltip, // 6
  31709. seriesUserOptions.tooltip // 7
  31710. );
  31711. // When shared tooltip, stickyTracking is true by default,
  31712. // unless user says otherwise.
  31713. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptionsType.stickyTracking, userPlotOptionsSeries.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  31714. true :
  31715. options.stickyTracking));
  31716. // Delete marker object if not allowed (#1125)
  31717. if (typeOptions.marker === null) {
  31718. delete options.marker;
  31719. }
  31720. // Handle color zones
  31721. this.zoneAxis = options.zoneAxis;
  31722. const zones = this.zones = (options.zones || []).slice();
  31723. if ((options.negativeColor || options.negativeFillColor) &&
  31724. !options.zones) {
  31725. zone = {
  31726. value: options[this.zoneAxis + 'Threshold'] ||
  31727. options.threshold ||
  31728. 0,
  31729. className: 'highcharts-negative'
  31730. };
  31731. if (!styledMode) {
  31732. zone.color = options.negativeColor;
  31733. zone.fillColor = options.negativeFillColor;
  31734. }
  31735. zones.push(zone);
  31736. }
  31737. if (zones.length) { // Push one extra zone for the rest
  31738. if (defined(zones[zones.length - 1].value)) {
  31739. zones.push(styledMode ? {} : {
  31740. color: this.color,
  31741. fillColor: this.fillColor
  31742. });
  31743. }
  31744. }
  31745. fireEvent(this, 'afterSetOptions', { options: options });
  31746. return options;
  31747. }
  31748. /**
  31749. * Return series name in "Series {Number}" format or the one defined by
  31750. * a user. This method can be simply overridden as series name format
  31751. * can vary (e.g. technical indicators).
  31752. *
  31753. * @function Highcharts.Series#getName
  31754. *
  31755. * @return {string}
  31756. * The series name.
  31757. */
  31758. getName() {
  31759. // #4119
  31760. return pick(this.options.name, 'Series ' + (this.index + 1));
  31761. }
  31762. /**
  31763. * @private
  31764. * @function Highcharts.Series#getCyclic
  31765. */
  31766. getCyclic(prop, value, defaults) {
  31767. const chart = this.chart, indexName = `${prop}Index`, counterName = `${prop}Counter`, len = (
  31768. // Symbol count
  31769. (defaults === null || defaults === void 0 ? void 0 : defaults.length) ||
  31770. // Color count
  31771. chart.options.chart.colorCount);
  31772. let i, setting;
  31773. if (!value) {
  31774. // Pick up either the colorIndex option, or the series.colorIndex
  31775. // after Series.update()
  31776. setting = pick(prop === 'color' ? this.options.colorIndex : void 0, this[indexName]);
  31777. if (defined(setting)) { // after Series.update()
  31778. i = setting;
  31779. }
  31780. else {
  31781. // #6138
  31782. if (!chart.series.length) {
  31783. chart[counterName] = 0;
  31784. }
  31785. i = chart[counterName] % len;
  31786. chart[counterName] += 1;
  31787. }
  31788. if (defaults) {
  31789. value = defaults[i];
  31790. }
  31791. }
  31792. // Set the colorIndex
  31793. if (typeof i !== 'undefined') {
  31794. this[indexName] = i;
  31795. }
  31796. this[prop] = value;
  31797. }
  31798. /**
  31799. * Get the series' color based on either the options or pulled from
  31800. * global options.
  31801. *
  31802. * @private
  31803. * @function Highcharts.Series#getColor
  31804. */
  31805. getColor() {
  31806. if (this.chart.styledMode) {
  31807. this.getCyclic('color');
  31808. }
  31809. else if (this.options.colorByPoint) {
  31810. this.color = "#cccccc" /* Palette.neutralColor20 */;
  31811. }
  31812. else {
  31813. this.getCyclic('color', this.options.color ||
  31814. defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
  31815. }
  31816. }
  31817. /**
  31818. * Get all points' instances created for this series.
  31819. *
  31820. * @private
  31821. * @function Highcharts.Series#getPointsCollection
  31822. */
  31823. getPointsCollection() {
  31824. return (this.hasGroupedData ? this.points : this.data) || [];
  31825. }
  31826. /**
  31827. * Get the series' symbol based on either the options or pulled from
  31828. * global options.
  31829. *
  31830. * @private
  31831. * @function Highcharts.Series#getSymbol
  31832. */
  31833. getSymbol() {
  31834. const seriesMarkerOption = this.options.marker;
  31835. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  31836. }
  31837. /**
  31838. * Finds the index of an existing point that matches the given point
  31839. * options.
  31840. *
  31841. * @private
  31842. * @function Highcharts.Series#findPointIndex
  31843. * @param {Highcharts.PointOptionsObject} optionsObject
  31844. * The options of the point.
  31845. * @param {number} fromIndex
  31846. * The index to start searching from, used for optimizing series with
  31847. * required sorting.
  31848. * @return {number|undefined}
  31849. * Returns the index of a matching point, or undefined if no match is found.
  31850. */
  31851. findPointIndex(optionsObject, fromIndex) {
  31852. const id = optionsObject.id, x = optionsObject.x, oldData = this.points, dataSorting = this.options.dataSorting;
  31853. let matchingPoint, matchedById, pointIndex;
  31854. if (id) {
  31855. const item = this.chart.get(id);
  31856. if (item instanceof Point) {
  31857. matchingPoint = item;
  31858. }
  31859. }
  31860. else if (this.linkedParent ||
  31861. this.enabledDataSorting ||
  31862. this.options.relativeXValue) {
  31863. let matcher = (oldPoint) => !oldPoint.touched &&
  31864. oldPoint.index === optionsObject.index;
  31865. if (dataSorting && dataSorting.matchByName) {
  31866. matcher = (oldPoint) => !oldPoint.touched &&
  31867. oldPoint.name === optionsObject.name;
  31868. }
  31869. else if (this.options.relativeXValue) {
  31870. matcher = (oldPoint) => !oldPoint.touched &&
  31871. oldPoint.options.x === optionsObject.x;
  31872. }
  31873. matchingPoint = find(oldData, matcher);
  31874. // Add unmatched point as a new point
  31875. if (!matchingPoint) {
  31876. return void 0;
  31877. }
  31878. }
  31879. if (matchingPoint) {
  31880. pointIndex = matchingPoint && matchingPoint.index;
  31881. if (typeof pointIndex !== 'undefined') {
  31882. matchedById = true;
  31883. }
  31884. }
  31885. // Search for the same X in the existing data set
  31886. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  31887. pointIndex = this.xData.indexOf(x, fromIndex);
  31888. }
  31889. // Reduce pointIndex if data is cropped
  31890. if (pointIndex !== -1 &&
  31891. typeof pointIndex !== 'undefined' &&
  31892. this.cropped) {
  31893. pointIndex = (pointIndex >= this.cropStart) ?
  31894. pointIndex - this.cropStart : pointIndex;
  31895. }
  31896. if (!matchedById &&
  31897. isNumber(pointIndex) &&
  31898. oldData[pointIndex] && oldData[pointIndex].touched) {
  31899. pointIndex = void 0;
  31900. }
  31901. return pointIndex;
  31902. }
  31903. /**
  31904. * Internal function called from setData. If the point count is the same
  31905. * as it was, or if there are overlapping X values, just run
  31906. * Point.update which is cheaper, allows animation, and keeps references
  31907. * to points. This also allows adding or removing points if the X-es
  31908. * don't match.
  31909. *
  31910. * @private
  31911. * @function Highcharts.Series#updateData
  31912. */
  31913. updateData(data, animation) {
  31914. const options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], requireSorting = this.requireSorting, equalLength = data.length === oldData.length;
  31915. let hasUpdatedByKey, i, point, lastIndex, succeeded = true;
  31916. this.xIncrement = null;
  31917. // Iterate the new data
  31918. data.forEach(function (pointOptions, i) {
  31919. const optionsObject = (defined(pointOptions) &&
  31920. this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
  31921. let pointIndex;
  31922. // Get the x of the new data point
  31923. const x = optionsObject.x, id = optionsObject.id;
  31924. if (id || isNumber(x)) {
  31925. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  31926. // Matching X not found
  31927. // or used already due to ununique x values (#8995),
  31928. // add point (but later)
  31929. if (pointIndex === -1 ||
  31930. typeof pointIndex === 'undefined') {
  31931. pointsToAdd.push(pointOptions);
  31932. // Matching X found, update
  31933. }
  31934. else if (oldData[pointIndex] &&
  31935. pointOptions !== options.data[pointIndex]) {
  31936. oldData[pointIndex].update(pointOptions, false, null, false);
  31937. // Mark it touched, below we will remove all points that
  31938. // are not touched.
  31939. oldData[pointIndex].touched = true;
  31940. // Speed optimize by only searching after last known
  31941. // index. Performs ~20% bettor on large data sets.
  31942. if (requireSorting) {
  31943. lastIndex = pointIndex + 1;
  31944. }
  31945. // Point exists, no changes, don't remove it
  31946. }
  31947. else if (oldData[pointIndex]) {
  31948. oldData[pointIndex].touched = true;
  31949. }
  31950. // If the length is equal and some of the nodes had a
  31951. // match in the same position, we don't want to remove
  31952. // non-matches.
  31953. if (!equalLength ||
  31954. i !== pointIndex ||
  31955. (dataSorting && dataSorting.enabled) ||
  31956. this.hasDerivedData) {
  31957. hasUpdatedByKey = true;
  31958. }
  31959. }
  31960. else {
  31961. // Gather all points that are not matched
  31962. pointsToAdd.push(pointOptions);
  31963. }
  31964. }, this);
  31965. // Remove points that don't exist in the updated data set
  31966. if (hasUpdatedByKey) {
  31967. i = oldData.length;
  31968. while (i--) {
  31969. point = oldData[i];
  31970. if (point && !point.touched && point.remove) {
  31971. point.remove(false, animation);
  31972. }
  31973. }
  31974. // If we did not find keys (ids or x-values), and the length is the
  31975. // same, update one-to-one
  31976. }
  31977. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  31978. data.forEach(function (point, i) {
  31979. // .update doesn't exist on a linked, hidden series (#3709)
  31980. // (#10187)
  31981. if (point !== oldData[i].y && !oldData[i].destroyed) {
  31982. oldData[i].update(point, false, null, false);
  31983. }
  31984. });
  31985. // Don't add new points since those configs are used above
  31986. pointsToAdd.length = 0;
  31987. // Did not succeed in updating data
  31988. }
  31989. else {
  31990. succeeded = false;
  31991. }
  31992. oldData.forEach(function (point) {
  31993. if (point) {
  31994. point.touched = false;
  31995. }
  31996. });
  31997. if (!succeeded) {
  31998. return false;
  31999. }
  32000. // Add new points
  32001. pointsToAdd.forEach(function (point) {
  32002. this.addPoint(point, false, null, null, false);
  32003. }, this);
  32004. if (this.xIncrement === null &&
  32005. this.xData &&
  32006. this.xData.length) {
  32007. this.xIncrement = arrayMax(this.xData);
  32008. this.autoIncrement();
  32009. }
  32010. return true;
  32011. }
  32012. /**
  32013. * Apply a new set of data to the series and optionally redraw it. The
  32014. * new data array is passed by reference (except in case of
  32015. * `updatePoints`), and may later be mutated when updating the chart
  32016. * data.
  32017. *
  32018. * Note the difference in behaviour when setting the same amount of
  32019. * points, or a different amount of points, as handled by the
  32020. * `updatePoints` parameter.
  32021. *
  32022. * @sample highcharts/members/series-setdata/
  32023. * Set new data from a button
  32024. * @sample highcharts/members/series-setdata-pie/
  32025. * Set data in a pie
  32026. * @sample stock/members/series-setdata/
  32027. * Set new data in Highcharts Stock
  32028. * @sample maps/members/series-setdata/
  32029. * Set new data in Highmaps
  32030. *
  32031. * @function Highcharts.Series#setData
  32032. *
  32033. * @param {Array<Highcharts.PointOptionsType>} data
  32034. * Takes an array of data in the same format as described under
  32035. * `series.{type}.data` for the given series type, for example a
  32036. * line series would take data in the form described under
  32037. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  32038. *
  32039. * @param {boolean} [redraw=true]
  32040. * Whether to redraw the chart after the series is altered. If
  32041. * doing more operations on the chart, it is a good idea to set
  32042. * redraw to false and call {@link Chart#redraw} after.
  32043. *
  32044. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  32045. * When the updated data is the same length as the existing data,
  32046. * points will be updated by default, and animation visualizes
  32047. * how the points are changed. Set false to disable animation, or
  32048. * a configuration object to set duration or easing.
  32049. *
  32050. * @param {boolean} [updatePoints=true]
  32051. * When this is true, points will be updated instead of replaced
  32052. * whenever possible. This occurs a) when the updated data is the
  32053. * same length as the existing data, b) when points are matched
  32054. * by their id's, or c) when points can be matched by X values.
  32055. * This allows updating with animation and performs better. In
  32056. * this case, the original array is not passed by reference. Set
  32057. * `false` to prevent.
  32058. */
  32059. setData(data, redraw = true, animation, updatePoints) {
  32060. var _a;
  32061. const series = this, oldData = series.points, oldDataLength = (oldData && oldData.length) || 0, options = series.options, chart = series.chart, dataSorting = options.dataSorting, xAxis = series.xAxis, turboThreshold = options.turboThreshold, xData = this.xData, yData = this.yData, pointArrayMap = series.pointArrayMap, valueCount = pointArrayMap && pointArrayMap.length, keys = options.keys;
  32062. let i, pt, updatedData, indexOfX = 0, indexOfY = 1, firstPoint = null, copiedData;
  32063. if (!chart.options.chart.allowMutatingData) { // #4259
  32064. // Remove old reference
  32065. if (options.data) {
  32066. delete series.options.data;
  32067. }
  32068. if (series.userOptions.data) {
  32069. delete series.userOptions.data;
  32070. }
  32071. copiedData = merge(true, data);
  32072. }
  32073. data = copiedData || data || [];
  32074. const dataLength = data.length;
  32075. if (dataSorting && dataSorting.enabled) {
  32076. data = this.sortData(data);
  32077. }
  32078. // First try to run Point.update which is cheaper, allows animation,
  32079. // and keeps references to points.
  32080. if (chart.options.chart.allowMutatingData &&
  32081. updatePoints !== false &&
  32082. dataLength &&
  32083. oldDataLength &&
  32084. !series.cropped &&
  32085. !series.hasGroupedData &&
  32086. series.visible &&
  32087. // Soft updating has no benefit in boost, and causes JS error
  32088. // (#8355)
  32089. !series.boosted) {
  32090. updatedData = this.updateData(data, animation);
  32091. }
  32092. if (!updatedData) {
  32093. // Reset properties
  32094. series.xIncrement = null;
  32095. series.colorCounter = 0; // for series with colorByPoint (#1547)
  32096. // Update parallel arrays
  32097. this.parallelArrays.forEach(function (key) {
  32098. series[key + 'Data'].length = 0;
  32099. });
  32100. // In turbo mode, only one- or twodimensional arrays of numbers
  32101. // are allowed. The first value is tested, and we assume that
  32102. // all the rest are defined the same way. Although the 'for'
  32103. // loops are similar, they are repeated inside each if-else
  32104. // conditional for max performance.
  32105. if (turboThreshold && dataLength > turboThreshold) {
  32106. firstPoint = series.getFirstValidPoint(data);
  32107. if (isNumber(firstPoint)) { // assume all points are numbers
  32108. for (i = 0; i < dataLength; i++) {
  32109. xData[i] = this.autoIncrement();
  32110. yData[i] = data[i];
  32111. }
  32112. // Assume all points are arrays when first point is
  32113. }
  32114. else if (isArray(firstPoint)) {
  32115. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  32116. if (firstPoint.length === valueCount) {
  32117. for (i = 0; i < dataLength; i++) {
  32118. xData[i] = this.autoIncrement();
  32119. yData[i] = data[i];
  32120. }
  32121. }
  32122. else {
  32123. for (i = 0; i < dataLength; i++) {
  32124. pt = data[i];
  32125. xData[i] = pt[0];
  32126. yData[i] =
  32127. pt.slice(1, valueCount + 1);
  32128. }
  32129. }
  32130. }
  32131. else { // [x, y]
  32132. if (keys) {
  32133. indexOfX = keys.indexOf('x');
  32134. indexOfY = keys.indexOf('y');
  32135. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  32136. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  32137. }
  32138. if (firstPoint.length === 1) {
  32139. indexOfY = 0;
  32140. }
  32141. if (indexOfX === indexOfY) {
  32142. for (i = 0; i < dataLength; i++) {
  32143. xData[i] = this.autoIncrement();
  32144. yData[i] = data[i][indexOfY];
  32145. }
  32146. }
  32147. else {
  32148. for (i = 0; i < dataLength; i++) {
  32149. pt = data[i];
  32150. xData[i] = pt[indexOfX];
  32151. yData[i] = pt[indexOfY];
  32152. }
  32153. }
  32154. }
  32155. }
  32156. else {
  32157. // Highcharts expects configs to be numbers or arrays in
  32158. // turbo mode
  32159. error(12, false, chart);
  32160. }
  32161. }
  32162. else {
  32163. for (i = 0; i < dataLength; i++) {
  32164. pt = { series: series };
  32165. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  32166. series.updateParallelArrays(pt, i);
  32167. }
  32168. }
  32169. // Forgetting to cast strings to numbers is a common caveat when
  32170. // handling CSV or JSON
  32171. if (yData && isString(yData[0])) {
  32172. error(14, true, chart);
  32173. }
  32174. series.data = [];
  32175. series.options.data = series.userOptions.data = data;
  32176. // destroy old points
  32177. i = oldDataLength;
  32178. while (i--) {
  32179. (_a = oldData[i]) === null || _a === void 0 ? void 0 : _a.destroy();
  32180. }
  32181. // reset minRange (#878)
  32182. if (xAxis) {
  32183. xAxis.minRange = xAxis.userMinRange;
  32184. }
  32185. // redraw
  32186. series.isDirty = chart.isDirtyBox = true;
  32187. series.isDirtyData = !!oldData;
  32188. animation = false;
  32189. }
  32190. // Typically for pie series, points need to be processed and
  32191. // generated prior to rendering the legend
  32192. if (options.legendType === 'point') {
  32193. this.processData();
  32194. this.generatePoints();
  32195. }
  32196. if (redraw) {
  32197. chart.redraw(animation);
  32198. }
  32199. }
  32200. /**
  32201. * Internal function to sort series data
  32202. *
  32203. * @private
  32204. * @function Highcharts.Series#sortData
  32205. * @param {Array<Highcharts.PointOptionsType>} data
  32206. * Force data grouping.
  32207. */
  32208. sortData(data) {
  32209. const series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', getPointOptionsObject = function (series, pointOptions) {
  32210. return (defined(pointOptions) &&
  32211. series.pointClass.prototype.optionsToObject.call({
  32212. series: series
  32213. }, pointOptions)) || {};
  32214. };
  32215. data.forEach(function (pointOptions, i) {
  32216. data[i] = getPointOptionsObject(series, pointOptions);
  32217. data[i].index = i;
  32218. }, this);
  32219. // Sorting
  32220. const sortedData = data.concat().sort((a, b) => {
  32221. const aValue = getNestedProperty(sortKey, a);
  32222. const bValue = getNestedProperty(sortKey, b);
  32223. return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
  32224. });
  32225. // Set x value depending on the position in the array
  32226. sortedData.forEach(function (point, i) {
  32227. point.x = i;
  32228. }, this);
  32229. // Set the same x for linked series points if they don't have their
  32230. // own sorting
  32231. if (series.linkedSeries) {
  32232. series.linkedSeries.forEach(function (linkedSeries) {
  32233. const options = linkedSeries.options, seriesData = options.data;
  32234. if ((!options.dataSorting ||
  32235. !options.dataSorting.enabled) &&
  32236. seriesData) {
  32237. seriesData.forEach(function (pointOptions, i) {
  32238. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  32239. if (data[i]) {
  32240. seriesData[i].x = data[i].x;
  32241. seriesData[i].index = i;
  32242. }
  32243. });
  32244. linkedSeries.setData(seriesData, false);
  32245. }
  32246. });
  32247. }
  32248. return data;
  32249. }
  32250. /**
  32251. * Internal function to process the data by cropping away unused data
  32252. * points if the series is longer than the crop threshold. This saves
  32253. * computing time for large series.
  32254. *
  32255. * @private
  32256. * @function Highcharts.Series#getProcessedData
  32257. * @param {boolean} [forceExtremesFromAll]
  32258. * Force getting extremes of a total series data range.
  32259. */
  32260. getProcessedData(forceExtremesFromAll) {
  32261. const series = this, xAxis = series.xAxis, options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
  32262. series.getExtremesFromAll ||
  32263. options.getExtremesFromAll, // #4599
  32264. logarithmic = xAxis === null || xAxis === void 0 ? void 0 : xAxis.logarithmic, isCartesian = series.isCartesian;
  32265. let croppedData, cropped, cropStart = 0, xExtremes, min, max,
  32266. // copied during slice operation:
  32267. processedXData = series.xData, processedYData = series.yData, updatingNames = false;
  32268. const dataLength = processedXData.length;
  32269. if (xAxis) {
  32270. // corrected for log axis (#3053)
  32271. xExtremes = xAxis.getExtremes();
  32272. min = xExtremes.min;
  32273. max = xExtremes.max;
  32274. updatingNames = !!(xAxis.categories && !xAxis.names.length);
  32275. }
  32276. // optionally filter out points outside the plot area
  32277. if (isCartesian &&
  32278. series.sorted &&
  32279. !getExtremesFromAll &&
  32280. (!cropThreshold ||
  32281. dataLength > cropThreshold ||
  32282. series.forceCrop)) {
  32283. // it's outside current extremes
  32284. if (processedXData[dataLength - 1] < min ||
  32285. processedXData[0] > max) {
  32286. processedXData = [];
  32287. processedYData = [];
  32288. // only crop if it's actually spilling out
  32289. }
  32290. else if (series.yData && (processedXData[0] < min ||
  32291. processedXData[dataLength - 1] > max)) {
  32292. croppedData = this.cropData(series.xData, series.yData, min, max);
  32293. processedXData = croppedData.xData;
  32294. processedYData = croppedData.yData;
  32295. cropStart = croppedData.start;
  32296. cropped = true;
  32297. }
  32298. }
  32299. // Find the closest distance between processed points
  32300. const closestPointRange = getClosestDistance([
  32301. logarithmic ?
  32302. processedXData.map(logarithmic.log2lin) :
  32303. processedXData
  32304. ],
  32305. // Unsorted data is not supported by the line tooltip, as well as
  32306. // data grouping and navigation in Stock charts (#725) and width
  32307. // calculation of columns (#1900). Avoid warning during the
  32308. // premature processing pass in updateNames (#16104).
  32309. () => (series.requireSorting &&
  32310. !updatingNames &&
  32311. error(15, false, series.chart)));
  32312. return {
  32313. xData: processedXData,
  32314. yData: processedYData,
  32315. cropped: cropped,
  32316. cropStart: cropStart,
  32317. closestPointRange: closestPointRange
  32318. };
  32319. }
  32320. /**
  32321. * Internal function to apply processed data.
  32322. * In Highcharts Stock, this function is extended to provide data grouping.
  32323. *
  32324. * @private
  32325. * @function Highcharts.Series#processData
  32326. * @param {boolean} [force]
  32327. * Force data grouping.
  32328. */
  32329. processData(force) {
  32330. const series = this, xAxis = series.xAxis;
  32331. // If the series data or axes haven't changed, don't go through
  32332. // this. Return false to pass the message on to override methods
  32333. // like in data grouping.
  32334. if (series.isCartesian &&
  32335. !series.isDirty &&
  32336. !xAxis.isDirty &&
  32337. !series.yAxis.isDirty &&
  32338. !force) {
  32339. return false;
  32340. }
  32341. const processedData = series.getProcessedData();
  32342. // Record the properties
  32343. series.cropped = processedData.cropped; // undefined or true
  32344. series.cropStart = processedData.cropStart;
  32345. series.processedXData = processedData.xData;
  32346. series.processedYData = processedData.yData;
  32347. series.closestPointRange = (series.basePointRange = processedData.closestPointRange);
  32348. fireEvent(series, 'afterProcessData');
  32349. }
  32350. /**
  32351. * Iterate over xData and crop values between min and max. Returns
  32352. * object containing crop start/end cropped xData with corresponding
  32353. * part of yData, dataMin and dataMax within the cropped range.
  32354. *
  32355. * @private
  32356. * @function Highcharts.Series#cropData
  32357. */
  32358. cropData(xData, yData, min, max, cropShoulder) {
  32359. const dataLength = xData.length;
  32360. let i, j, cropStart = 0, cropEnd = dataLength;
  32361. // line-type series need one point outside
  32362. cropShoulder = pick(cropShoulder, this.cropShoulder);
  32363. // iterate up to find slice start
  32364. for (i = 0; i < dataLength; i++) {
  32365. if (xData[i] >= min) {
  32366. cropStart = Math.max(0, i - cropShoulder);
  32367. break;
  32368. }
  32369. }
  32370. // proceed to find slice end
  32371. for (j = i; j < dataLength; j++) {
  32372. if (xData[j] > max) {
  32373. cropEnd = j + cropShoulder;
  32374. break;
  32375. }
  32376. }
  32377. return {
  32378. xData: xData.slice(cropStart, cropEnd),
  32379. yData: yData.slice(cropStart, cropEnd),
  32380. start: cropStart,
  32381. end: cropEnd
  32382. };
  32383. }
  32384. /**
  32385. * Generate the data point after the data has been processed by cropping
  32386. * away unused points and optionally grouped in Highcharts Stock.
  32387. *
  32388. * @private
  32389. * @function Highcharts.Series#generatePoints
  32390. */
  32391. generatePoints() {
  32392. const series = this, options = series.options, dataOptions = (series.processedData || options.data), processedXData = series.processedXData, processedYData = series.processedYData, PointClass = series.pointClass, processedDataLength = processedXData.length, cropStart = series.cropStart || 0, hasGroupedData = series.hasGroupedData, keys = options.keys, points = [], groupCropStartIndex = (options.dataGrouping &&
  32393. options.dataGrouping.groupAll ?
  32394. cropStart :
  32395. 0);
  32396. let dataLength, cursor, point, i, data = series.data;
  32397. if (!data && !hasGroupedData) {
  32398. const arr = [];
  32399. arr.length = dataOptions.length;
  32400. data = series.data = arr;
  32401. }
  32402. if (keys && hasGroupedData) {
  32403. // grouped data has already applied keys (#6590)
  32404. series.options.keys = false;
  32405. }
  32406. for (i = 0; i < processedDataLength; i++) {
  32407. cursor = cropStart + i;
  32408. if (!hasGroupedData) {
  32409. point = data[cursor];
  32410. // #970:
  32411. if (!point &&
  32412. typeof dataOptions[cursor] !== 'undefined') {
  32413. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  32414. }
  32415. }
  32416. else {
  32417. // splat the y data in case of ohlc data array
  32418. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  32419. point.dataGroup = series.groupMap[groupCropStartIndex + i];
  32420. if (point.dataGroup.options) {
  32421. point.options = point.dataGroup.options;
  32422. extend(point, point.dataGroup.options);
  32423. // Collision of props and options (#9770)
  32424. delete point.dataLabels;
  32425. }
  32426. }
  32427. if (point) { // #6279
  32428. /**
  32429. * Contains the point's index in the `Series.points` array.
  32430. *
  32431. * @name Highcharts.Point#index
  32432. * @type {number}
  32433. * @readonly
  32434. */
  32435. // For faster access in Point.update
  32436. point.index = hasGroupedData ?
  32437. (groupCropStartIndex + i) : cursor;
  32438. points[i] = point;
  32439. }
  32440. }
  32441. // restore keys options (#6590)
  32442. series.options.keys = keys;
  32443. // Hide cropped-away points - this only runs when the number of
  32444. // points is above cropThreshold, or when swithching view from
  32445. // non-grouped data to grouped data (#637)
  32446. if (data &&
  32447. (processedDataLength !== (dataLength = data.length) ||
  32448. hasGroupedData)) {
  32449. for (i = 0; i < dataLength; i++) {
  32450. // when has grouped data, clear all points
  32451. if (i === cropStart && !hasGroupedData) {
  32452. i += processedDataLength;
  32453. }
  32454. if (data[i]) {
  32455. data[i].destroyElements();
  32456. data[i].plotX = void 0; // #1003
  32457. }
  32458. }
  32459. }
  32460. /**
  32461. * Read only. An array containing those values converted to points.
  32462. * In case the series data length exceeds the `cropThreshold`, or if
  32463. * the data is grouped, `series.data` doesn't contain all the
  32464. * points. Also, in case a series is hidden, the `data` array may be
  32465. * empty. To access raw values, `series.options.data` will always be
  32466. * up to date. `Series.data` only contains the points that have been
  32467. * created on demand. To modify the data, use
  32468. * {@link Highcharts.Series#setData} or
  32469. * {@link Highcharts.Point#update}.
  32470. *
  32471. * @see Series.points
  32472. *
  32473. * @name Highcharts.Series#data
  32474. * @type {Array<Highcharts.Point>}
  32475. */
  32476. series.data = data;
  32477. /**
  32478. * An array containing all currently visible point objects. In case
  32479. * of cropping, the cropped-away points are not part of this array.
  32480. * The `series.points` array starts at `series.cropStart` compared
  32481. * to `series.data` and `series.options.data`. If however the series
  32482. * data is grouped, these can't be correlated one to one. To modify
  32483. * the data, use {@link Highcharts.Series#setData} or
  32484. * {@link Highcharts.Point#update}.
  32485. *
  32486. * @name Highcharts.Series#points
  32487. * @type {Array<Highcharts.Point>}
  32488. */
  32489. series.points = points;
  32490. fireEvent(this, 'afterGeneratePoints');
  32491. }
  32492. /**
  32493. * Get current X extremes for the visible data.
  32494. *
  32495. * @private
  32496. * @function Highcharts.Series#getXExtremes
  32497. * @param {Array<number>} xData
  32498. * The data to inspect. Defaults to the current data within the visible
  32499. * range.
  32500. */
  32501. getXExtremes(xData) {
  32502. return {
  32503. min: arrayMin(xData),
  32504. max: arrayMax(xData)
  32505. };
  32506. }
  32507. /**
  32508. * Calculate Y extremes for the visible data. The result is returned
  32509. * as an object with `dataMin` and `dataMax` properties.
  32510. *
  32511. * @private
  32512. * @function Highcharts.Series#getExtremes
  32513. * @param {Array<number>} [yData]
  32514. * The data to inspect. Defaults to the current data within the visible
  32515. * range.
  32516. * @param {boolean} [forceExtremesFromAll]
  32517. * Force getting extremes of a total series data range.
  32518. */
  32519. getExtremes(yData, forceExtremesFromAll) {
  32520. const xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, activeYData = [],
  32521. // Handle X outside the viewed area. This does not work with
  32522. // non-sorted data like scatter (#7639).
  32523. shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false;
  32524. // #2117, need to compensate for log X axis
  32525. let xExtremes, validValue, withinRange, x, y, i, j, xMin = 0, xMax = 0, activeCounter = 0;
  32526. yData = yData || this.stackedYData || this.processedYData || [];
  32527. const yDataLength = yData.length;
  32528. if (xAxis) {
  32529. xExtremes = xAxis.getExtremes();
  32530. xMin = xExtremes.min;
  32531. xMax = xExtremes.max;
  32532. }
  32533. for (i = 0; i < yDataLength; i++) {
  32534. x = xData[i];
  32535. y = yData[i];
  32536. // For points within the visible range, including the first
  32537. // point outside the visible range (#7061), consider y extremes.
  32538. validValue = ((isNumber(y) || isArray(y)) &&
  32539. ((y.length || y > 0) || !positiveValuesOnly));
  32540. withinRange = (forceExtremesFromAll ||
  32541. this.getExtremesFromAll ||
  32542. this.options.getExtremesFromAll ||
  32543. this.cropped ||
  32544. !xAxis || // for colorAxis support
  32545. ((xData[i + shoulder] || x) >= xMin &&
  32546. (xData[i - shoulder] || x) <= xMax));
  32547. if (validValue && withinRange) {
  32548. j = y.length;
  32549. if (j) { // array, like ohlc or range data
  32550. while (j--) {
  32551. if (isNumber(y[j])) { // #7380, #11513
  32552. activeYData[activeCounter++] = y[j];
  32553. }
  32554. }
  32555. }
  32556. else {
  32557. activeYData[activeCounter++] = y;
  32558. }
  32559. }
  32560. }
  32561. const dataExtremes = {
  32562. activeYData,
  32563. dataMin: arrayMin(activeYData),
  32564. dataMax: arrayMax(activeYData)
  32565. };
  32566. fireEvent(this, 'afterGetExtremes', { dataExtremes });
  32567. return dataExtremes;
  32568. }
  32569. /**
  32570. * Set the current data extremes as `dataMin` and `dataMax` on the
  32571. * Series item. Use this only when the series properties should be
  32572. * updated.
  32573. *
  32574. * @private
  32575. * @function Highcharts.Series#applyExtremes
  32576. */
  32577. applyExtremes() {
  32578. const dataExtremes = this.getExtremes();
  32579. /**
  32580. * Contains the minimum value of the series' data point. Some series
  32581. * types like `networkgraph` do not support this property as they
  32582. * lack a `y`-value.
  32583. * @name Highcharts.Series#dataMin
  32584. * @type {number|undefined}
  32585. * @readonly
  32586. */
  32587. this.dataMin = dataExtremes.dataMin;
  32588. /**
  32589. * Contains the maximum value of the series' data point. Some series
  32590. * types like `networkgraph` do not support this property as they
  32591. * lack a `y`-value.
  32592. * @name Highcharts.Series#dataMax
  32593. * @type {number|undefined}
  32594. * @readonly
  32595. */
  32596. this.dataMax = dataExtremes.dataMax;
  32597. return dataExtremes;
  32598. }
  32599. /**
  32600. * Find and return the first non null point in the data
  32601. *
  32602. * @private
  32603. * @function Highcharts.Series.getFirstValidPoint
  32604. * @param {Array<Highcharts.PointOptionsType>} data
  32605. * Array of options for points
  32606. */
  32607. getFirstValidPoint(data) {
  32608. const dataLength = data.length;
  32609. let i = 0, firstPoint = null;
  32610. while (firstPoint === null && i < dataLength) {
  32611. firstPoint = data[i];
  32612. i++;
  32613. }
  32614. return firstPoint;
  32615. }
  32616. /**
  32617. * Translate data points from raw data values to chart specific
  32618. * positioning data needed later in the `drawPoints` and `drawGraph`
  32619. * functions. This function can be overridden in plugins and custom
  32620. * series type implementations.
  32621. *
  32622. * @function Highcharts.Series#translate
  32623. *
  32624. * @emits Highcharts.Series#events:translate
  32625. */
  32626. translate() {
  32627. var _a;
  32628. if (!this.processedXData) { // hidden series
  32629. this.processData();
  32630. }
  32631. this.generatePoints();
  32632. const series = this, options = series.options, stacking = options.stacking, xAxis = series.xAxis, categories = xAxis.categories, enabledDataSorting = series.enabledDataSorting, yAxis = series.yAxis, points = series.points, dataLength = points.length, pointPlacement = series.pointPlacementToXValue(), // #7860
  32633. dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0;
  32634. let i, plotX, lastPlotX, stackIndicator, closestPointRangePx = Number.MAX_VALUE;
  32635. /**
  32636. * Plotted coordinates need to be within a limited range. Drawing
  32637. * too far outside the viewport causes various rendering issues
  32638. * (#3201, #3923, #7555).
  32639. * @private
  32640. */
  32641. function limitedRange(val) {
  32642. return clamp(val, -1e5, 1e5);
  32643. }
  32644. // Translate each point
  32645. for (i = 0; i < dataLength; i++) {
  32646. const point = points[i], xValue = point.x;
  32647. let stackItem, stackValues, yValue = point.y, lowValue = point.low;
  32648. const stacks = stacking && ((_a = yAxis.stacking) === null || _a === void 0 ? void 0 : _a.stacks[(series.negStacks &&
  32649. yValue <
  32650. (stackThreshold ? 0 : threshold) ?
  32651. '-' :
  32652. '') + series.stackKey]);
  32653. plotX = xAxis.translate(// #3923
  32654. xValue, false, false, false, true, pointPlacement);
  32655. /**
  32656. * The translated X value for the point in terms of pixels. Relative
  32657. * to the X axis position if the series has one, otherwise relative
  32658. * to the plot area. Depending on the series type this value might
  32659. * not be defined.
  32660. * @name Highcharts.Point#plotX
  32661. * @type {number|undefined}
  32662. */
  32663. point.plotX = isNumber(plotX) ? correctFloat(// #5236
  32664. limitedRange(plotX) // #3923
  32665. ) : void 0;
  32666. // Calculate the bottom y value for stacked series
  32667. if (stacking &&
  32668. series.visible &&
  32669. stacks &&
  32670. stacks[xValue]) {
  32671. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  32672. if (!point.isNull && stackIndicator.key) {
  32673. stackItem = stacks[xValue];
  32674. stackValues = stackItem.points[stackIndicator.key];
  32675. }
  32676. if (stackItem && isArray(stackValues)) {
  32677. lowValue = stackValues[0];
  32678. yValue = stackValues[1];
  32679. if (lowValue === stackThreshold &&
  32680. stackIndicator.key === stacks[xValue].base) {
  32681. lowValue = pick(isNumber(threshold) ? threshold : yAxis.min);
  32682. }
  32683. // #1200, #1232
  32684. if (yAxis.positiveValuesOnly &&
  32685. defined(lowValue) &&
  32686. lowValue <= 0) {
  32687. lowValue = void 0;
  32688. }
  32689. point.total = point.stackTotal = pick(stackItem.total);
  32690. point.percentage = defined(point.y) && stackItem.total ?
  32691. (point.y / stackItem.total * 100) : void 0;
  32692. point.stackY = yValue;
  32693. // in case of variwide series (where widths of points are
  32694. // different in most cases), stack labels are positioned
  32695. // wrongly, so the call of the setOffset is omited here and
  32696. // labels are correctly positioned later, at the end of the
  32697. // variwide's translate function (#10962)
  32698. if (!series.irregularWidths) {
  32699. stackItem.setOffset(series.pointXOffset || 0, series.barW || 0, void 0, void 0, void 0, series.xAxis);
  32700. }
  32701. }
  32702. }
  32703. // Set translated yBottom or remove it
  32704. point.yBottom = defined(lowValue) ?
  32705. limitedRange(yAxis.translate(lowValue, false, true, false, true)) :
  32706. void 0;
  32707. // General hook, used for Highcharts Stock compare and cumulative
  32708. if (series.dataModify) {
  32709. yValue = series.dataModify.modifyValue(yValue, i);
  32710. }
  32711. // Set the the plotY value, reset it for redraws #3201, #18422
  32712. let plotY;
  32713. if (isNumber(yValue) && point.plotX !== void 0) {
  32714. plotY = yAxis.translate(yValue, false, true, false, true);
  32715. plotY = isNumber(plotY) ? limitedRange(plotY) : void 0;
  32716. }
  32717. /**
  32718. * The translated Y value for the point in terms of pixels. Relative
  32719. * to the Y axis position if the series has one, otherwise relative
  32720. * to the plot area. Depending on the series type this value might
  32721. * not be defined.
  32722. * @name Highcharts.Point#plotY
  32723. * @type {number|undefined}
  32724. */
  32725. point.plotY = plotY;
  32726. point.isInside = this.isPointInside(point);
  32727. // Set client related positions for mouse tracking
  32728. point.clientX = dynamicallyPlaced ?
  32729. correctFloat(xAxis.translate(xValue, false, false, false, true, pointPlacement)) :
  32730. plotX; // #1514, #5383, #5518
  32731. // Negative points #19028
  32732. point.negative = (point.y || 0) < (threshold || 0);
  32733. // some API data
  32734. point.category = pick(categories && categories[point.x], point.x);
  32735. // Determine auto enabling of markers (#3635, #5099)
  32736. if (!point.isNull && point.visible !== false) {
  32737. if (typeof lastPlotX !== 'undefined') {
  32738. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  32739. }
  32740. lastPlotX = plotX;
  32741. }
  32742. // Find point zone
  32743. point.zone = this.zones.length ? point.getZone() : void 0;
  32744. // Animate new points with data sorting
  32745. if (!point.graphic && series.group && enabledDataSorting) {
  32746. point.isNew = true;
  32747. }
  32748. }
  32749. series.closestPointRangePx = closestPointRangePx;
  32750. fireEvent(this, 'afterTranslate');
  32751. }
  32752. /**
  32753. * Return the series points with null points filtered out.
  32754. *
  32755. * @function Highcharts.Series#getValidPoints
  32756. *
  32757. * @param {Array<Highcharts.Point>} [points]
  32758. * The points to inspect, defaults to {@link Series.points}.
  32759. *
  32760. * @param {boolean} [insideOnly=false]
  32761. * Whether to inspect only the points that are inside the visible view.
  32762. *
  32763. * @param {boolean} [allowNull=false]
  32764. * Whether to allow null points to pass as valid points.
  32765. *
  32766. * @return {Array<Highcharts.Point>}
  32767. * The valid points.
  32768. */
  32769. getValidPoints(points, insideOnly, allowNull) {
  32770. const chart = this.chart;
  32771. // #3916, #5029, #5085
  32772. return (points || this.points || []).filter(function (point) {
  32773. const { plotX, plotY } = point,
  32774. // Undefined plotY is treated as null when negative values
  32775. // in log axis (#18422)
  32776. asNull = !allowNull && (point.isNull || !isNumber(plotY));
  32777. if (asNull || (insideOnly && !chart.isInsidePlot(plotX, plotY, { inverted: chart.inverted }))) {
  32778. return false;
  32779. }
  32780. return point.visible !== false;
  32781. });
  32782. }
  32783. /**
  32784. * Get the clipping for the series. Could be called for a series to
  32785. * initiate animating the clip or to set the final clip (only width
  32786. * and x).
  32787. *
  32788. * @private
  32789. * @function Highcharts.Series#getClip
  32790. */
  32791. getClipBox() {
  32792. const { chart, xAxis, yAxis } = this;
  32793. // If no axes on the series, use global clipBox
  32794. const seriesBox = merge(chart.clipBox);
  32795. // Otherwise, use clipBox.width which is corrected for plotBorderWidth
  32796. // and clipOffset
  32797. if (xAxis && xAxis.len !== chart.plotSizeX) {
  32798. seriesBox.width = xAxis.len;
  32799. }
  32800. if (yAxis && yAxis.len !== chart.plotSizeY) {
  32801. seriesBox.height = yAxis.len;
  32802. }
  32803. return seriesBox;
  32804. }
  32805. /**
  32806. * Get the shared clip key, creating it if it doesn't exist.
  32807. *
  32808. * @private
  32809. * @function Highcharts.Series#getSharedClipKey
  32810. */
  32811. getSharedClipKey() {
  32812. this.sharedClipKey = (this.options.xAxis || 0) + ',' +
  32813. (this.options.yAxis || 0);
  32814. return this.sharedClipKey;
  32815. }
  32816. /**
  32817. * Set the clipping for the series. For animated series the clip is later
  32818. * modified.
  32819. *
  32820. * @private
  32821. * @function Highcharts.Series#setClip
  32822. */
  32823. setClip() {
  32824. const { chart, group, markerGroup } = this, sharedClips = chart.sharedClips, renderer = chart.renderer, clipBox = this.getClipBox(), sharedClipKey = this.getSharedClipKey(); // #4526
  32825. let clipRect = sharedClips[sharedClipKey];
  32826. // If a clipping rectangle for the same set of axes does not exist,
  32827. // create it
  32828. if (!clipRect) {
  32829. sharedClips[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  32830. // When setting chart size, or when the series is rendered again before
  32831. // starting animating, in compliance to a responsive rule
  32832. }
  32833. else {
  32834. clipRect.animate(clipBox);
  32835. }
  32836. if (group) {
  32837. // When clip is false, reset to no clip after animation
  32838. group.clip(this.options.clip === false ? void 0 : clipRect);
  32839. }
  32840. // Unclip temporary animation clip
  32841. if (markerGroup) {
  32842. markerGroup.clip();
  32843. }
  32844. }
  32845. /**
  32846. * Animate in the series. Called internally twice. First with the `init`
  32847. * parameter set to true, which sets up the initial state of the
  32848. * animation. Then when ready, it is called with the `init` parameter
  32849. * undefined, in order to perform the actual animation.
  32850. *
  32851. * @function Highcharts.Series#animate
  32852. *
  32853. * @param {boolean} [init]
  32854. * Initialize the animation.
  32855. */
  32856. animate(init) {
  32857. const { chart, group, markerGroup } = this, inverted = chart.inverted, animation = animObject(this.options.animation),
  32858. // The key for temporary animation clips
  32859. animationClipKey = [
  32860. this.getSharedClipKey(),
  32861. animation.duration,
  32862. animation.easing,
  32863. animation.defer
  32864. ].join(',');
  32865. let animationClipRect = chart.sharedClips[animationClipKey], markerAnimationClipRect = chart.sharedClips[animationClipKey + 'm'];
  32866. // Initialize the animation. Set up the clipping rectangle.
  32867. if (init && group) {
  32868. const clipBox = this.getClipBox();
  32869. // Create temporary animation clips
  32870. if (!animationClipRect) {
  32871. clipBox.width = 0;
  32872. if (inverted) {
  32873. clipBox.x = chart.plotHeight;
  32874. }
  32875. animationClipRect = chart.renderer.clipRect(clipBox);
  32876. chart.sharedClips[animationClipKey] = animationClipRect;
  32877. // The marker clip box. The number 99 is a safe margin to avoid
  32878. // markers being clipped during animation.
  32879. const markerClipBox = {
  32880. x: inverted ? -99 : -99,
  32881. y: inverted ? -99 : -99,
  32882. width: inverted ? chart.plotWidth + 199 : 99,
  32883. height: inverted ? 99 : chart.plotHeight + 199
  32884. };
  32885. markerAnimationClipRect = chart.renderer.clipRect(markerClipBox);
  32886. chart.sharedClips[animationClipKey + 'm'] = markerAnimationClipRect;
  32887. }
  32888. else {
  32889. // When height changes during animation, typically due to
  32890. // responsive settings
  32891. animationClipRect.attr('height', clipBox.height);
  32892. }
  32893. group.clip(animationClipRect);
  32894. if (markerGroup) {
  32895. markerGroup.clip(markerAnimationClipRect);
  32896. }
  32897. // Run the animation
  32898. }
  32899. else if (animationClipRect &&
  32900. // Only first series in this pane
  32901. !animationClipRect.hasClass('highcharts-animating')) {
  32902. const finalBox = this.getClipBox(), step = animation.step;
  32903. // Only do this when there are actually markers
  32904. if (markerGroup && markerGroup.element.childNodes.length) {
  32905. // To provide as smooth animation as possible, update the marker
  32906. // group clipping in steps of the main group animation
  32907. animation.step = function (val, fx) {
  32908. if (step) {
  32909. step.apply(fx, arguments);
  32910. }
  32911. if (fx.prop === 'width' &&
  32912. markerAnimationClipRect &&
  32913. markerAnimationClipRect.element) {
  32914. markerAnimationClipRect.attr(inverted ? 'height' : 'width', val + 99);
  32915. }
  32916. };
  32917. }
  32918. animationClipRect
  32919. .addClass('highcharts-animating')
  32920. .animate(finalBox, animation);
  32921. }
  32922. }
  32923. /**
  32924. * This runs after animation to land on the final plot clipping.
  32925. *
  32926. * @private
  32927. * @function Highcharts.Series#afterAnimate
  32928. *
  32929. * @emits Highcharts.Series#event:afterAnimate
  32930. */
  32931. afterAnimate() {
  32932. this.setClip();
  32933. // Destroy temporary clip rectangles that are no longer in use
  32934. objectEach(this.chart.sharedClips, (clip, key, sharedClips) => {
  32935. if (clip && !this.chart.container.querySelector(`[clip-path="url(#${clip.id})"]`)) {
  32936. clip.destroy();
  32937. delete sharedClips[key];
  32938. }
  32939. });
  32940. this.finishedAnimating = true;
  32941. fireEvent(this, 'afterAnimate');
  32942. }
  32943. /**
  32944. * Draw the markers for line-like series types, and columns or other
  32945. * graphical representation for {@link Point} objects for other series
  32946. * types. The resulting element is typically stored as
  32947. * {@link Point.graphic}, and is created on the first call and updated
  32948. * and moved on subsequent calls.
  32949. *
  32950. * @function Highcharts.Series#drawPoints
  32951. */
  32952. drawPoints(points = this.points) {
  32953. const series = this, chart = series.chart, styledMode = chart.styledMode, { colorAxis, options } = series, seriesMarkerOptions = options.marker, markerGroup = series[series.specialGroup || 'markerGroup'], xAxis = series.xAxis, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
  32954. // Use larger or equal as radius is null in bubbles (#6321)
  32955. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  32956. seriesMarkerOptions.radius));
  32957. let i, point, graphic, verb, pointMarkerOptions, hasPointMarker, markerAttribs;
  32958. if (seriesMarkerOptions.enabled !== false ||
  32959. series._hasPointMarkers) {
  32960. for (i = 0; i < points.length; i++) {
  32961. point = points[i];
  32962. graphic = point.graphic;
  32963. verb = graphic ? 'animate' : 'attr';
  32964. pointMarkerOptions = point.marker || {};
  32965. hasPointMarker = !!point.marker;
  32966. const shouldDrawMarker = ((globallyEnabled &&
  32967. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  32968. // only draw the point if y is defined
  32969. if (shouldDrawMarker) {
  32970. // Shortcuts
  32971. const symbol = pick(pointMarkerOptions.symbol, series.symbol, 'rect');
  32972. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  32973. // Set starting position for point sliding animation.
  32974. if (series.enabledDataSorting) {
  32975. point.startXPos = xAxis.reversed ?
  32976. -(markerAttribs.width || 0) :
  32977. xAxis.width;
  32978. }
  32979. const isInside = point.isInside !== false;
  32980. if (!graphic &&
  32981. isInside &&
  32982. ((markerAttribs.width || 0) > 0 || point.hasImage)) {
  32983. /**
  32984. * SVG graphic representing the point in the chart. In
  32985. * some cases it may be a hidden graphic to improve
  32986. * accessibility.
  32987. *
  32988. * Typically this is a simple shape, like a `rect`
  32989. * for column charts or `path` for line markers, but
  32990. * for some complex series types like boxplot or 3D
  32991. * charts, the graphic may be a `g` element
  32992. * containing other shapes. The graphic is generated
  32993. * the first time {@link Series#drawPoints} runs,
  32994. * and updated and moved on subsequent runs.
  32995. *
  32996. * @see Highcharts.Point#graphics
  32997. *
  32998. * @name Highcharts.Point#graphic
  32999. * @type {Highcharts.SVGElement|undefined}
  33000. */
  33001. point.graphic = graphic = chart.renderer
  33002. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  33003. pointMarkerOptions :
  33004. seriesMarkerOptions)
  33005. .add(markerGroup);
  33006. // Sliding animation for new points
  33007. if (series.enabledDataSorting &&
  33008. chart.hasRendered) {
  33009. graphic.attr({
  33010. x: point.startXPos
  33011. });
  33012. verb = 'animate';
  33013. }
  33014. }
  33015. if (graphic && verb === 'animate') { // update
  33016. // Since the marker group isn't clipped, each
  33017. // individual marker must be toggled
  33018. graphic[isInside ? 'show' : 'hide'](isInside)
  33019. .animate(markerAttribs);
  33020. }
  33021. // Presentational attributes
  33022. if (graphic) {
  33023. const pointAttr = series.pointAttribs(point, ((styledMode || !point.selected) ?
  33024. void 0 :
  33025. 'select'));
  33026. if (!styledMode) {
  33027. graphic[verb](pointAttr);
  33028. }
  33029. else if (colorAxis) { // #14114
  33030. graphic['css']({
  33031. fill: pointAttr.fill
  33032. });
  33033. }
  33034. }
  33035. if (graphic) {
  33036. graphic.addClass(point.getClassName(), true);
  33037. }
  33038. }
  33039. else if (graphic) {
  33040. point.graphic = graphic.destroy(); // #1269
  33041. }
  33042. }
  33043. }
  33044. }
  33045. /**
  33046. * Get non-presentational attributes for a point. Used internally for
  33047. * both styled mode and classic. Can be overridden for different series
  33048. * types.
  33049. *
  33050. * @see Series#pointAttribs
  33051. *
  33052. * @function Highcharts.Series#markerAttribs
  33053. *
  33054. * @param {Highcharts.Point} point
  33055. * The Point to inspect.
  33056. *
  33057. * @param {string} [state]
  33058. * The state, can be either `hover`, `select` or undefined.
  33059. *
  33060. * @return {Highcharts.SVGAttributes}
  33061. * A hash containing those attributes that are not settable from CSS.
  33062. */
  33063. markerAttribs(point, state) {
  33064. const seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
  33065. seriesMarkerOptions.symbol), attribs = {};
  33066. let seriesStateOptions, pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions && seriesMarkerOptions.radius);
  33067. // Handle hover and select states
  33068. if (state) {
  33069. seriesStateOptions = seriesMarkerOptions.states[state];
  33070. pointStateOptions = pointMarkerOptions.states &&
  33071. pointMarkerOptions.states[state];
  33072. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius && radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  33073. 0));
  33074. }
  33075. point.hasImage = symbol && symbol.indexOf('url') === 0;
  33076. if (point.hasImage) {
  33077. radius = 0; // and subsequently width and height is not set
  33078. }
  33079. const pos = point.pos();
  33080. if (isNumber(radius) && pos) {
  33081. attribs.x = pos[0] - radius;
  33082. attribs.y = pos[1] - radius;
  33083. if (seriesOptions.crisp) {
  33084. // Math.floor for #1843:
  33085. attribs.x = Math.floor(attribs.x);
  33086. }
  33087. }
  33088. if (radius) {
  33089. attribs.width = attribs.height = 2 * radius;
  33090. }
  33091. return attribs;
  33092. }
  33093. /**
  33094. * Internal function to get presentational attributes for each point.
  33095. * Unlike {@link Series#markerAttribs}, this function should return
  33096. * those attributes that can also be set in CSS. In styled mode,
  33097. * `pointAttribs` won't be called.
  33098. *
  33099. * @private
  33100. * @function Highcharts.Series#pointAttribs
  33101. *
  33102. * @param {Highcharts.Point} [point]
  33103. * The point instance to inspect.
  33104. *
  33105. * @param {string} [state]
  33106. * The point state, can be either `hover`, `select` or 'normal'. If
  33107. * undefined, normal state is assumed.
  33108. *
  33109. * @return {Highcharts.SVGAttributes}
  33110. * The presentational attributes to be set on the point.
  33111. */
  33112. pointAttribs(point, state) {
  33113. const seriesMarkerOptions = this.options.marker, pointOptions = point && point.options, pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}), pointColorOption = pointOptions && pointOptions.color, pointColor = point && point.color, zoneColor = point && point.zone && point.zone.color;
  33114. let seriesStateOptions, pointStateOptions, color = this.color, fill, stroke, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), opacity = 1;
  33115. color = (pointColorOption ||
  33116. zoneColor ||
  33117. pointColor ||
  33118. color);
  33119. fill = (pointMarkerOptions.fillColor ||
  33120. seriesMarkerOptions.fillColor ||
  33121. color);
  33122. stroke = (pointMarkerOptions.lineColor ||
  33123. seriesMarkerOptions.lineColor ||
  33124. color);
  33125. // Handle hover and select states
  33126. state = state || 'normal';
  33127. if (state) {
  33128. seriesStateOptions = (seriesMarkerOptions.states[state] || {});
  33129. pointStateOptions = (pointMarkerOptions.states &&
  33130. pointMarkerOptions.states[state]) || {};
  33131. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  33132. fill = (pointStateOptions.fillColor ||
  33133. seriesStateOptions.fillColor ||
  33134. fill);
  33135. stroke = (pointStateOptions.lineColor ||
  33136. seriesStateOptions.lineColor ||
  33137. stroke);
  33138. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  33139. }
  33140. return {
  33141. 'stroke': stroke,
  33142. 'stroke-width': strokeWidth,
  33143. 'fill': fill,
  33144. 'opacity': opacity
  33145. };
  33146. }
  33147. /**
  33148. * Clear DOM objects and free up memory.
  33149. *
  33150. * @private
  33151. * @function Highcharts.Series#destroy
  33152. *
  33153. * @emits Highcharts.Series#event:destroy
  33154. */
  33155. destroy(keepEventsForUpdate) {
  33156. const series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), data = series.data || [];
  33157. let destroy, i, point, axis;
  33158. // add event hook
  33159. fireEvent(series, 'destroy', { keepEventsForUpdate });
  33160. // remove events
  33161. this.removeEvents(keepEventsForUpdate);
  33162. // erase from axes
  33163. (series.axisTypes || []).forEach(function (AXIS) {
  33164. axis = series[AXIS];
  33165. if (axis && axis.series) {
  33166. erase(axis.series, series);
  33167. axis.isDirty = axis.forceRedraw = true;
  33168. }
  33169. });
  33170. // remove legend items
  33171. if (series.legendItem) {
  33172. series.chart.legend.destroyItem(series);
  33173. }
  33174. // destroy all points with their elements
  33175. i = data.length;
  33176. while (i--) {
  33177. point = data[i];
  33178. if (point && point.destroy) {
  33179. point.destroy();
  33180. }
  33181. }
  33182. if (series.clips) {
  33183. series.clips.forEach((clip) => clip.destroy());
  33184. }
  33185. // Clear the animation timeout if we are destroying the series
  33186. // during initial animation
  33187. U.clearTimeout(series.animationTimeout);
  33188. // Destroy all SVGElements associated to the series
  33189. objectEach(series, function (val, prop) {
  33190. // Survive provides a hook for not destroying
  33191. if (val instanceof SVGElement && !val.survive) {
  33192. // issue 134 workaround
  33193. destroy = issue134 && prop === 'group' ?
  33194. 'hide' :
  33195. 'destroy';
  33196. val[destroy]();
  33197. }
  33198. });
  33199. // remove from hoverSeries
  33200. if (chart.hoverSeries === series) {
  33201. chart.hoverSeries = void 0;
  33202. }
  33203. erase(chart.series, series);
  33204. chart.orderItems('series');
  33205. // clear all members
  33206. objectEach(series, function (val, prop) {
  33207. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  33208. delete series[prop];
  33209. }
  33210. });
  33211. }
  33212. /**
  33213. * Clip the graphs into zones for colors and styling.
  33214. *
  33215. * @private
  33216. * @function Highcharts.Series#applyZones
  33217. */
  33218. applyZones() {
  33219. const series = this, chart = this.chart, renderer = chart.renderer, zones = this.zones, clips = (this.clips || []), graph = this.graph, area = this.area, plotSizeMax = Math.max(chart.plotWidth, chart.plotHeight), axis = this[(this.zoneAxis || 'y') + 'Axis'], inverted = chart.inverted;
  33220. let translatedFrom, translatedTo, clipAttr, extremes, reversed, horiz, pxRange, pxPosMin, pxPosMax, zoneArea, zoneGraph, ignoreZones = false;
  33221. if (zones.length &&
  33222. (graph || area) &&
  33223. axis &&
  33224. typeof axis.min !== 'undefined') {
  33225. reversed = axis.reversed;
  33226. horiz = axis.horiz;
  33227. // The use of the Color Threshold assumes there are no gaps
  33228. // so it is safe to hide the original graph and area
  33229. // unless it is not waterfall series, then use showLine property
  33230. // to set lines between columns to be visible (#7862)
  33231. if (graph && !this.showLine) {
  33232. graph.hide();
  33233. }
  33234. if (area) {
  33235. area.hide();
  33236. }
  33237. // Create the clips
  33238. extremes = axis.getExtremes();
  33239. zones.forEach(function (threshold, i) {
  33240. translatedFrom = reversed ?
  33241. (horiz ? chart.plotWidth : 0) :
  33242. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  33243. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, plotSizeMax);
  33244. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, plotSizeMax);
  33245. if (ignoreZones) {
  33246. translatedFrom = translatedTo =
  33247. axis.toPixels(extremes.max);
  33248. }
  33249. pxRange = Math.abs(translatedFrom - translatedTo);
  33250. pxPosMin = Math.min(translatedFrom, translatedTo);
  33251. pxPosMax = Math.max(translatedFrom, translatedTo);
  33252. if (axis.isXAxis) {
  33253. clipAttr = {
  33254. x: inverted ? pxPosMax : pxPosMin,
  33255. y: 0,
  33256. width: pxRange,
  33257. height: plotSizeMax
  33258. };
  33259. if (!horiz) {
  33260. clipAttr.x = chart.plotHeight - clipAttr.x;
  33261. }
  33262. }
  33263. else {
  33264. clipAttr = {
  33265. x: 0,
  33266. y: inverted ? pxPosMax : pxPosMin,
  33267. width: plotSizeMax,
  33268. height: pxRange
  33269. };
  33270. if (horiz) {
  33271. clipAttr.y = chart.plotWidth - clipAttr.y;
  33272. }
  33273. }
  33274. if (clips[i]) {
  33275. clips[i].animate(clipAttr);
  33276. }
  33277. else {
  33278. clips[i] = renderer.clipRect(clipAttr);
  33279. }
  33280. // when no data, graph zone is not applied and after setData
  33281. // clip was ignored. As a result, it should be applied each
  33282. // time.
  33283. zoneArea = series['zone-area-' + i];
  33284. zoneGraph = series['zone-graph-' + i];
  33285. if (graph && zoneGraph) {
  33286. zoneGraph.clip(clips[i]);
  33287. }
  33288. if (area && zoneArea) {
  33289. zoneArea.clip(clips[i]);
  33290. }
  33291. // if this zone extends out of the axis, ignore the others
  33292. ignoreZones = threshold.value > extremes.max;
  33293. // Clear translatedTo for indicators
  33294. if (series.resetZones && translatedTo === 0) {
  33295. translatedTo = void 0;
  33296. }
  33297. });
  33298. this.clips = clips;
  33299. }
  33300. else if (series.visible) {
  33301. // If zones were removed, restore graph and area
  33302. if (graph) {
  33303. graph.show();
  33304. }
  33305. if (area) {
  33306. area.show();
  33307. }
  33308. }
  33309. }
  33310. /**
  33311. * General abstraction for creating plot groups like series.group,
  33312. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  33313. * the group will only be adjusted to the updated plot size.
  33314. *
  33315. * @private
  33316. * @function Highcharts.Series#plotGroup
  33317. */
  33318. plotGroup(prop, name, visibility, zIndex, parent) {
  33319. let group = this[prop];
  33320. const isNew = !group, attrs = {
  33321. visibility,
  33322. zIndex: zIndex || 0.1 // Pointer logic uses this
  33323. };
  33324. // Avoid setting undefined opacity, or in styled mode
  33325. if (typeof this.opacity !== 'undefined' &&
  33326. !this.chart.styledMode && this.state !== 'inactive' // #13719
  33327. ) {
  33328. attrs.opacity = this.opacity;
  33329. }
  33330. // Generate it on first call
  33331. if (isNew) {
  33332. this[prop] = group = this.chart.renderer
  33333. .g()
  33334. .add(parent);
  33335. }
  33336. // Add the class names, and replace existing ones as response to
  33337. // Series.update (#6660)
  33338. group.addClass(('highcharts-' + name +
  33339. ' highcharts-series-' + this.index +
  33340. ' highcharts-' + this.type + '-series ' +
  33341. (defined(this.colorIndex) ?
  33342. 'highcharts-color-' + this.colorIndex + ' ' :
  33343. '') +
  33344. (this.options.className || '') +
  33345. (group.hasClass('highcharts-tracker') ?
  33346. ' highcharts-tracker' :
  33347. '')), true);
  33348. // Place it on first and subsequent (redraw) calls
  33349. group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox(name));
  33350. return group;
  33351. }
  33352. /**
  33353. * Get the translation and scale for the plot area of this series.
  33354. *
  33355. * @function Highcharts.Series#getPlotBox
  33356. */
  33357. getPlotBox(name) {
  33358. let horAxis = this.xAxis, vertAxis = this.yAxis;
  33359. const chart = this.chart, inverted = (chart.inverted &&
  33360. !chart.polar &&
  33361. horAxis &&
  33362. this.invertible !== false &&
  33363. name === 'series');
  33364. // Swap axes for inverted (#2339)
  33365. if (chart.inverted) {
  33366. horAxis = vertAxis;
  33367. vertAxis = this.xAxis;
  33368. }
  33369. return {
  33370. translateX: horAxis ? horAxis.left : chart.plotLeft,
  33371. translateY: vertAxis ? vertAxis.top : chart.plotTop,
  33372. rotation: inverted ? 90 : 0,
  33373. rotationOriginX: inverted ?
  33374. (horAxis.len - vertAxis.len) / 2 :
  33375. 0,
  33376. rotationOriginY: inverted ?
  33377. (horAxis.len + vertAxis.len) / 2 :
  33378. 0,
  33379. scaleX: inverted ? -1 : 1,
  33380. scaleY: 1
  33381. };
  33382. }
  33383. /**
  33384. * Removes the event handlers attached previously with addEvents.
  33385. * @private
  33386. * @function Highcharts.Series#removeEvents
  33387. */
  33388. removeEvents(keepEventsForUpdate) {
  33389. const series = this;
  33390. if (!keepEventsForUpdate) {
  33391. // remove all events
  33392. removeEvent(series);
  33393. }
  33394. if (series.eventsToUnbind.length) {
  33395. // remove only internal events for proper update
  33396. // #12355 - solves problem with multiple destroy events
  33397. series.eventsToUnbind.forEach(function (unbind) {
  33398. unbind();
  33399. });
  33400. series.eventsToUnbind.length = 0;
  33401. }
  33402. }
  33403. /**
  33404. * Render the graph and markers. Called internally when first rendering
  33405. * and later when redrawing the chart. This function can be extended in
  33406. * plugins, but normally shouldn't be called directly.
  33407. *
  33408. * @function Highcharts.Series#render
  33409. *
  33410. * @emits Highcharts.Series#event:afterRender
  33411. */
  33412. render() {
  33413. const series = this, chart = series.chart, options = series.options, animOptions = animObject(options.animation), visibility = series.visible ?
  33414. 'inherit' : 'hidden', // #2597
  33415. zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
  33416. let animDuration = (!series.finishedAnimating) ?
  33417. animOptions.duration : 0;
  33418. fireEvent(this, 'render');
  33419. // the group
  33420. const group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  33421. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  33422. // Initial clipping, applies to columns etc. (#3839).
  33423. if (options.clip !== false) {
  33424. series.setClip();
  33425. }
  33426. // Initialize the animation
  33427. if (series.animate && animDuration) {
  33428. series.animate(true);
  33429. }
  33430. // Draw the graph if any
  33431. if (series.drawGraph) {
  33432. series.drawGraph();
  33433. series.applyZones();
  33434. }
  33435. // Draw the points
  33436. if (series.visible) {
  33437. series.drawPoints();
  33438. }
  33439. // Draw the data labels
  33440. if (series.drawDataLabels) {
  33441. series.drawDataLabels();
  33442. }
  33443. // In pie charts, slices are added to the DOM, but actual rendering
  33444. // is postponed until labels reserved their space
  33445. if (series.redrawPoints) {
  33446. series.redrawPoints();
  33447. }
  33448. // Draw the mouse tracking area
  33449. if (series.drawTracker &&
  33450. options.enableMouseTracking) {
  33451. series.drawTracker();
  33452. }
  33453. // Run the animation
  33454. if (series.animate && animDuration) {
  33455. series.animate();
  33456. }
  33457. // Call the afterAnimate function on animation complete (but don't
  33458. // overwrite the animation.complete option which should be available
  33459. // to the user).
  33460. if (!hasRendered) {
  33461. // Additional time if defer is defined before afterAnimate
  33462. // will be triggered
  33463. if (animDuration && animOptions.defer) {
  33464. animDuration += animOptions.defer;
  33465. }
  33466. series.animationTimeout = syncTimeout(function () {
  33467. series.afterAnimate();
  33468. }, animDuration || 0);
  33469. }
  33470. // Means data is in accordance with what you see
  33471. series.isDirty = false;
  33472. // (See #322) series.isDirty = series.isDirtyData = false; // means
  33473. // data is in accordance with what you see
  33474. series.hasRendered = true;
  33475. fireEvent(series, 'afterRender');
  33476. }
  33477. /**
  33478. * Redraw the series. This function is called internally from
  33479. * `chart.redraw` and normally shouldn't be called directly.
  33480. * @private
  33481. * @function Highcharts.Series#redraw
  33482. */
  33483. redraw() {
  33484. // Cache it here as it is set to false in render, but used after
  33485. const wasDirty = this.isDirty || this.isDirtyData;
  33486. this.translate();
  33487. this.render();
  33488. if (wasDirty) { // #3868, #3945
  33489. delete this.kdTree;
  33490. }
  33491. }
  33492. /**
  33493. * Find the nearest point from a pointer event. This applies to series that
  33494. * use k-d-trees to get the nearest point. Native pointer events must be
  33495. * normalized using `Pointer.normalize`, that adds `chartX` and `chartY`
  33496. * properties.
  33497. *
  33498. * @sample highcharts/demo/synchronized-charts
  33499. * Synchronized charts with tooltips
  33500. *
  33501. * @function Highcharts.Series#searchPoint
  33502. *
  33503. * @param {Highcharts.PointerEvent} e
  33504. * The normalized pointer event
  33505. * @param {boolean} [compareX=false]
  33506. * Search only by the X value, not Y
  33507. *
  33508. * @return {Point|undefined}
  33509. * The closest point to the pointer event
  33510. */
  33511. searchPoint(e, compareX) {
  33512. const series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
  33513. return this.searchKDTree({
  33514. clientX: inverted ?
  33515. xAxis.len - e.chartY + xAxis.pos :
  33516. e.chartX - xAxis.pos,
  33517. plotY: inverted ?
  33518. yAxis.len - e.chartX + yAxis.pos :
  33519. e.chartY - yAxis.pos
  33520. }, compareX, e);
  33521. }
  33522. /**
  33523. * Build the k-d-tree that is used by mouse and touch interaction to get
  33524. * the closest point. Line-like series typically have a one-dimensional
  33525. * tree where points are searched along the X axis, while scatter-like
  33526. * series typically search in two dimensions, X and Y.
  33527. *
  33528. * @private
  33529. * @function Highcharts.Series#buildKDTree
  33530. */
  33531. buildKDTree(e) {
  33532. // Prevent multiple k-d-trees from being built simultaneously
  33533. // (#6235)
  33534. this.buildingKdTree = true;
  33535. const series = this, dimensions = series.options.findNearestPointBy
  33536. .indexOf('y') > -1 ? 2 : 1;
  33537. /**
  33538. * Internal function
  33539. * @private
  33540. */
  33541. function _kdtree(points, depth, dimensions) {
  33542. const length = points && points.length;
  33543. let axis, median;
  33544. if (length) {
  33545. // alternate between the axis
  33546. axis = series.kdAxisArray[depth % dimensions];
  33547. // sort point array
  33548. points.sort(function (a, b) {
  33549. return a[axis] - b[axis];
  33550. });
  33551. median = Math.floor(length / 2);
  33552. // build and return nod
  33553. return {
  33554. point: points[median],
  33555. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  33556. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  33557. };
  33558. }
  33559. }
  33560. /**
  33561. * Start the recursive build process with a clone of the points
  33562. * array and null points filtered out. (#3873)
  33563. * @private
  33564. */
  33565. function startRecursive() {
  33566. series.kdTree = _kdtree(series.getValidPoints(null,
  33567. // For line-type series restrict to plot area, but
  33568. // column-type series not (#3916, #4511)
  33569. !series.directTouch), dimensions, dimensions);
  33570. series.buildingKdTree = false;
  33571. }
  33572. delete series.kdTree;
  33573. // For testing tooltips, don't build async. Also if touchstart, we
  33574. // may be dealing with click events on mobile, so don't delay
  33575. // (#6817).
  33576. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  33577. }
  33578. /**
  33579. * @private
  33580. * @function Highcharts.Series#searchKDTree
  33581. */
  33582. searchKDTree(point, compareX, e) {
  33583. const series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
  33584. .indexOf('y') > -1 ? 2 : 1;
  33585. /**
  33586. * Set the one and two dimensional distance on the point object.
  33587. * @private
  33588. */
  33589. function setDistance(p1, p2) {
  33590. const x = (defined(p1[kdX]) &&
  33591. defined(p2[kdX])) ?
  33592. Math.pow(p1[kdX] - p2[kdX], 2) :
  33593. null, y = (defined(p1[kdY]) &&
  33594. defined(p2[kdY])) ?
  33595. Math.pow(p1[kdY] - p2[kdY], 2) :
  33596. null, r = (x || 0) + (y || 0);
  33597. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  33598. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  33599. }
  33600. /**
  33601. * @private
  33602. */
  33603. function _search(search, tree, depth, dimensions) {
  33604. const point = tree.point, axis = series.kdAxisArray[depth % dimensions];
  33605. let nPoint1, nPoint2, ret = point;
  33606. setDistance(search, point);
  33607. // Pick side based on distance to splitting point
  33608. const tdist = search[axis] - point[axis], sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
  33609. // End of tree
  33610. if (tree[sideA]) {
  33611. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  33612. ret = (nPoint1[kdComparer] <
  33613. ret[kdComparer] ?
  33614. nPoint1 :
  33615. point);
  33616. }
  33617. if (tree[sideB]) {
  33618. // compare distance to current best to splitting point to
  33619. // decide whether to check side B or not
  33620. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  33621. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  33622. ret = (nPoint2[kdComparer] <
  33623. ret[kdComparer] ?
  33624. nPoint2 :
  33625. ret);
  33626. }
  33627. }
  33628. return ret;
  33629. }
  33630. if (!this.kdTree && !this.buildingKdTree) {
  33631. this.buildKDTree(e);
  33632. }
  33633. if (this.kdTree) {
  33634. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  33635. }
  33636. }
  33637. /**
  33638. * @private
  33639. * @function Highcharts.Series#pointPlacementToXValue
  33640. */
  33641. pointPlacementToXValue() {
  33642. const { options: { pointPlacement, pointRange }, xAxis: axis } = this;
  33643. let factor = pointPlacement;
  33644. // Point placement is relative to each series pointRange (#5889)
  33645. if (factor === 'between') {
  33646. factor = axis.reversed ? -0.5 : 0.5; // #11955
  33647. }
  33648. return isNumber(factor) ?
  33649. factor * (pointRange || axis.pointRange) :
  33650. 0;
  33651. }
  33652. /**
  33653. * @private
  33654. * @function Highcharts.Series#isPointInside
  33655. */
  33656. isPointInside(point) {
  33657. const { chart, xAxis, yAxis } = this, isInside = (typeof point.plotY !== 'undefined' &&
  33658. typeof point.plotX !== 'undefined' &&
  33659. point.plotY >= 0 &&
  33660. point.plotY <= (yAxis ? yAxis.len : chart.plotHeight) &&
  33661. point.plotX >= 0 &&
  33662. point.plotX <= (xAxis ? xAxis.len : chart.plotWidth));
  33663. return isInside;
  33664. }
  33665. /**
  33666. * Draw the tracker object that sits above all data labels and markers to
  33667. * track mouse events on the graph or points. For the line type charts
  33668. * the tracker uses the same graphPath, but with a greater stroke width
  33669. * for better control.
  33670. * @private
  33671. */
  33672. drawTracker() {
  33673. const series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat(trackByArea ?
  33674. series.areaPath :
  33675. series.graphPath),
  33676. // trackerPathLength = trackerPath.length,
  33677. chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, onMouseOver = function (e) {
  33678. if (options.enableMouseTracking &&
  33679. chart.hoverSeries !== series) {
  33680. series.onMouseOver();
  33681. }
  33682. },
  33683. /*
  33684. * Empirical lowest possible opacities for TRACKER_FILL for an
  33685. * element to stay invisible but clickable
  33686. * IE9: 0.00000000001 (unlimited)
  33687. * IE10: 0.0001 (exporting only)
  33688. * FF: 0.00000000001 (unlimited)
  33689. * Chrome: 0.000001
  33690. * Safari: 0.000001
  33691. * Opera: 0.00000000001 (unlimited)
  33692. */
  33693. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  33694. let i;
  33695. // Draw the tracker
  33696. if (tracker) {
  33697. tracker.attr({ d: trackerPath });
  33698. }
  33699. else if (series.graph) { // create
  33700. series.tracker = renderer.path(trackerPath)
  33701. .attr({
  33702. visibility: series.visible ? 'inherit' : 'hidden',
  33703. zIndex: 2
  33704. })
  33705. .addClass(trackByArea ?
  33706. 'highcharts-tracker-area' :
  33707. 'highcharts-tracker-line')
  33708. .add(series.group);
  33709. if (!chart.styledMode) {
  33710. series.tracker.attr({
  33711. 'stroke-linecap': 'round',
  33712. 'stroke-linejoin': 'round',
  33713. stroke: TRACKER_FILL,
  33714. fill: trackByArea ? TRACKER_FILL : 'none',
  33715. 'stroke-width': series.graph.strokeWidth() +
  33716. (trackByArea ? 0 : 2 * snap)
  33717. });
  33718. }
  33719. // The tracker is added to the series group, which is clipped, but
  33720. // is covered by the marker group. So the marker group also needs to
  33721. // capture events.
  33722. [
  33723. series.tracker,
  33724. series.markerGroup,
  33725. series.dataLabelsGroup
  33726. ].forEach(function (tracker) {
  33727. if (tracker) {
  33728. tracker.addClass('highcharts-tracker')
  33729. .on('mouseover', onMouseOver)
  33730. .on('mouseout', function (e) {
  33731. pointer.onTrackerMouseOut(e);
  33732. });
  33733. if (options.cursor && !chart.styledMode) {
  33734. tracker.css({ cursor: options.cursor });
  33735. }
  33736. if (hasTouch) {
  33737. tracker.on('touchstart', onMouseOver);
  33738. }
  33739. }
  33740. });
  33741. }
  33742. fireEvent(this, 'afterDrawTracker');
  33743. }
  33744. /**
  33745. * Add a point to the series after render time. The point can be added at
  33746. * the end, or by giving it an X value, to the start or in the middle of the
  33747. * series.
  33748. *
  33749. * @sample highcharts/members/series-addpoint-append/
  33750. * Append point
  33751. * @sample highcharts/members/series-addpoint-append-and-shift/
  33752. * Append and shift
  33753. * @sample highcharts/members/series-addpoint-x-and-y/
  33754. * Both X and Y values given
  33755. * @sample highcharts/members/series-addpoint-pie/
  33756. * Append pie slice
  33757. * @sample stock/members/series-addpoint/
  33758. * Append 100 points in Highcharts Stock
  33759. * @sample stock/members/series-addpoint-shift/
  33760. * Append and shift in Highcharts Stock
  33761. * @sample maps/members/series-addpoint/
  33762. * Add a point in Highmaps
  33763. *
  33764. * @function Highcharts.Series#addPoint
  33765. *
  33766. * @param {Highcharts.PointOptionsType} options
  33767. * The point options. If options is a single number, a point with
  33768. * that y value is appended to the series. If it is an array, it will
  33769. * be interpreted as x and y values respectively. If it is an
  33770. * object, advanced options as outlined under `series.data` are
  33771. * applied.
  33772. *
  33773. * @param {boolean} [redraw=true]
  33774. * Whether to redraw the chart after the point is added. When adding
  33775. * more than one point, it is highly recommended that the redraw
  33776. * option be set to false, and instead {@link Chart#redraw} is
  33777. * explicitly called after the adding of points is finished.
  33778. * Otherwise, the chart will redraw after adding each point.
  33779. *
  33780. * @param {boolean} [shift=false]
  33781. * If true, a point is shifted off the start of the series as one is
  33782. * appended to the end.
  33783. *
  33784. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33785. * Whether to apply animation, and optionally animation
  33786. * configuration.
  33787. *
  33788. * @param {boolean} [withEvent=true]
  33789. * Used internally, whether to fire the series `addPoint` event.
  33790. *
  33791. * @emits Highcharts.Series#event:addPoint
  33792. */
  33793. addPoint(options, redraw, shift, animation, withEvent) {
  33794. const series = this, seriesOptions = series.options, data = series.data, chart = series.chart, xAxis = series.xAxis, names = xAxis && xAxis.hasNames && xAxis.names, dataOptions = seriesOptions.data, xData = series.xData;
  33795. let isInTheMiddle, i;
  33796. // Optional redraw, defaults to true
  33797. redraw = pick(redraw, true);
  33798. // Get options and push the point to xData, yData and series.options. In
  33799. // series.generatePoints the Point instance will be created on demand
  33800. // and pushed to the series.data array.
  33801. const point = { series: series };
  33802. series.pointClass.prototype.applyOptions.apply(point, [options]);
  33803. const x = point.x;
  33804. // Get the insertion point
  33805. i = xData.length;
  33806. if (series.requireSorting && x < xData[i - 1]) {
  33807. isInTheMiddle = true;
  33808. while (i && xData[i - 1] > x) {
  33809. i--;
  33810. }
  33811. }
  33812. // Insert undefined item
  33813. series.updateParallelArrays(point, 'splice', [i, 0, 0]);
  33814. // Update it
  33815. series.updateParallelArrays(point, i);
  33816. if (names && point.name) {
  33817. names[x] = point.name;
  33818. }
  33819. dataOptions.splice(i, 0, options);
  33820. if (isInTheMiddle ||
  33821. // When processedData is present we need to splice an empty slot
  33822. // into series.data, otherwise generatePoints won't pick it up.
  33823. series.processedData) {
  33824. series.data.splice(i, 0, null);
  33825. series.processData();
  33826. }
  33827. // Generate points to be added to the legend (#1329)
  33828. if (seriesOptions.legendType === 'point') {
  33829. series.generatePoints();
  33830. }
  33831. // Shift the first point off the parallel arrays
  33832. if (shift) {
  33833. if (data[0] && (data[0].remove)) {
  33834. data[0].remove(false);
  33835. }
  33836. else {
  33837. data.shift();
  33838. series.updateParallelArrays(point, 'shift');
  33839. dataOptions.shift();
  33840. }
  33841. }
  33842. // Fire event
  33843. if (withEvent !== false) {
  33844. fireEvent(series, 'addPoint', { point: point });
  33845. }
  33846. // redraw
  33847. series.isDirty = true;
  33848. series.isDirtyData = true;
  33849. if (redraw) {
  33850. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  33851. }
  33852. }
  33853. /**
  33854. * Remove a point from the series. Unlike the
  33855. * {@link Highcharts.Point#remove} method, this can also be done on a point
  33856. * that is not instanciated because it is outside the view or subject to
  33857. * Highcharts Stock data grouping.
  33858. *
  33859. * @sample highcharts/members/series-removepoint/
  33860. * Remove cropped point
  33861. *
  33862. * @function Highcharts.Series#removePoint
  33863. *
  33864. * @param {number} i
  33865. * The index of the point in the {@link Highcharts.Series.data|data}
  33866. * array.
  33867. *
  33868. * @param {boolean} [redraw=true]
  33869. * Whether to redraw the chart after the point is added. When
  33870. * removing more than one point, it is highly recommended that the
  33871. * `redraw` option be set to `false`, and instead {@link
  33872. * Highcharts.Chart#redraw} is explicitly called after the adding of
  33873. * points is finished.
  33874. *
  33875. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33876. * Whether and optionally how the series should be animated.
  33877. *
  33878. * @emits Highcharts.Point#event:remove
  33879. */
  33880. removePoint(i, redraw, animation) {
  33881. const series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  33882. if (points && points.length === data.length) { // #4935
  33883. points.splice(i, 1);
  33884. }
  33885. data.splice(i, 1);
  33886. series.options.data.splice(i, 1);
  33887. series.updateParallelArrays(point || { series: series }, 'splice', [i, 1]);
  33888. if (point) {
  33889. point.destroy();
  33890. }
  33891. // redraw
  33892. series.isDirty = true;
  33893. series.isDirtyData = true;
  33894. if (redraw) {
  33895. chart.redraw();
  33896. }
  33897. };
  33898. setAnimation(animation, chart);
  33899. redraw = pick(redraw, true);
  33900. // Fire the event with a default handler of removing the point
  33901. if (point) {
  33902. point.firePointEvent('remove', null, remove);
  33903. }
  33904. else {
  33905. remove();
  33906. }
  33907. }
  33908. /**
  33909. * Remove a series and optionally redraw the chart.
  33910. *
  33911. * @sample highcharts/members/series-remove/
  33912. * Remove first series from a button
  33913. *
  33914. * @function Highcharts.Series#remove
  33915. *
  33916. * @param {boolean} [redraw=true]
  33917. * Whether to redraw the chart or wait for an explicit call to
  33918. * {@link Highcharts.Chart#redraw}.
  33919. *
  33920. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33921. * Whether to apply animation, and optionally animation
  33922. * configuration.
  33923. *
  33924. * @param {boolean} [withEvent=true]
  33925. * Used internally, whether to fire the series `remove` event.
  33926. *
  33927. * @emits Highcharts.Series#event:remove
  33928. */
  33929. remove(redraw, animation, withEvent, keepEvents) {
  33930. const series = this, chart = series.chart;
  33931. /**
  33932. * @private
  33933. */
  33934. function remove() {
  33935. // Destroy elements
  33936. series.destroy(keepEvents);
  33937. // Redraw
  33938. chart.isDirtyLegend = chart.isDirtyBox = true;
  33939. chart.linkSeries(keepEvents);
  33940. if (pick(redraw, true)) {
  33941. chart.redraw(animation);
  33942. }
  33943. }
  33944. // Fire the event with a default handler of removing the point
  33945. if (withEvent !== false) {
  33946. fireEvent(series, 'remove', null, remove);
  33947. }
  33948. else {
  33949. remove();
  33950. }
  33951. }
  33952. /**
  33953. * Update the series with a new set of options. For a clean and precise
  33954. * handling of new options, all methods and elements from the series are
  33955. * removed, and it is initialized from scratch. Therefore, this method is
  33956. * more performance expensive than some other utility methods like {@link
  33957. * Series#setData} or {@link Series#setVisible}.
  33958. *
  33959. * Note that `Series.update` may mutate the passed `data` options.
  33960. *
  33961. * @sample highcharts/members/series-update/
  33962. * Updating series options
  33963. * @sample maps/members/series-update/
  33964. * Update series options in Highmaps
  33965. *
  33966. * @function Highcharts.Series#update
  33967. *
  33968. * @param {Highcharts.SeriesOptionsType} options
  33969. * New options that will be merged with the series' existing options.
  33970. *
  33971. * @param {boolean} [redraw=true]
  33972. * Whether to redraw the chart after the series is altered. If doing
  33973. * more operations on the chart, it is a good idea to set redraw to
  33974. * false and call {@link Chart#redraw} after.
  33975. *
  33976. * @emits Highcharts.Series#event:update
  33977. * @emits Highcharts.Series#event:afterUpdate
  33978. */
  33979. update(options, redraw) {
  33980. options = diffObjects(options, this.userOptions);
  33981. fireEvent(this, 'update', { options: options });
  33982. const series = this, chart = series.chart,
  33983. // must use user options when changing type because series.options
  33984. // is merged in with type specific plotOptions
  33985. oldOptions = series.userOptions, initialType = series.initialType || series.type, plotOptions = chart.options.plotOptions, initialSeriesProto = seriesTypes[initialType].prototype, groups = [
  33986. 'group',
  33987. 'markerGroup',
  33988. 'dataLabelsGroup',
  33989. 'transformGroup'
  33990. ],
  33991. // Animation must be enabled when calling update before the initial
  33992. // animation has first run. This happens when calling update
  33993. // directly after chart initialization, or when applying responsive
  33994. // rules (#6912).
  33995. animation = series.finishedAnimating && { animation: false }, kinds = {};
  33996. let seriesOptions, n, preserve = [
  33997. 'colorIndex',
  33998. 'eventOptions',
  33999. 'navigatorSeries',
  34000. 'symbolIndex',
  34001. 'baseSeries'
  34002. ], newType = (options.type ||
  34003. oldOptions.type ||
  34004. chart.options.chart.type);
  34005. const keepPoints = !(
  34006. // Indicators, histograms etc recalculate the data. It should be
  34007. // possible to omit this.
  34008. this.hasDerivedData ||
  34009. // New type requires new point classes
  34010. (newType && newType !== this.type) ||
  34011. // New options affecting how the data points are built
  34012. typeof options.pointStart !== 'undefined' ||
  34013. typeof options.pointInterval !== 'undefined' ||
  34014. typeof options.relativeXValue !== 'undefined' ||
  34015. options.joinBy ||
  34016. options.mapData || // #11636
  34017. // Changes to data grouping requires new points in new group
  34018. series.hasOptionChanged('dataGrouping') ||
  34019. series.hasOptionChanged('pointStart') ||
  34020. series.hasOptionChanged('pointInterval') ||
  34021. series.hasOptionChanged('pointIntervalUnit') ||
  34022. series.hasOptionChanged('keys'));
  34023. newType = newType || initialType;
  34024. if (keepPoints) {
  34025. preserve.push('data', 'isDirtyData', 'points', 'processedData', // #17057
  34026. 'processedXData', 'processedYData', 'xIncrement', 'cropped', '_hasPointMarkers', '_hasPointLabels', 'clips', // #15420
  34027. // Networkgraph (#14397)
  34028. 'nodes', 'layout',
  34029. // Treemap
  34030. 'level',
  34031. // Map specific, consider moving it to series-specific preserve-
  34032. // properties (#10617)
  34033. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  34034. if (options.visible !== false) {
  34035. preserve.push('area', 'graph');
  34036. }
  34037. series.parallelArrays.forEach(function (key) {
  34038. preserve.push(key + 'Data');
  34039. });
  34040. if (options.data) {
  34041. // setData uses dataSorting options so we need to update them
  34042. // earlier
  34043. if (options.dataSorting) {
  34044. extend(series.options.dataSorting, options.dataSorting);
  34045. }
  34046. this.setData(options.data, false);
  34047. }
  34048. }
  34049. // Do the merge, with some forced options
  34050. options = merge(oldOptions, animation, {
  34051. // When oldOptions.index is null it should't be cleared.
  34052. // Otherwise navigator series will have wrong indexes (#10193).
  34053. index: typeof oldOptions.index === 'undefined' ?
  34054. series.index : oldOptions.index,
  34055. pointStart: pick(
  34056. // when updating from blank (#7933)
  34057. (plotOptions &&
  34058. plotOptions.series &&
  34059. plotOptions.series.pointStart), oldOptions.pointStart,
  34060. // when updating after addPoint
  34061. series.xData[0])
  34062. }, (!keepPoints && { data: series.options.data }), options);
  34063. // Merge does not merge arrays, but replaces them. Since points were
  34064. // updated, `series.options.data` has correct merged options, use it:
  34065. if (keepPoints && options.data) {
  34066. options.data = series.options.data;
  34067. }
  34068. // Make sure preserved properties are not destroyed (#3094)
  34069. preserve = groups.concat(preserve);
  34070. preserve.forEach(function (prop) {
  34071. preserve[prop] = series[prop];
  34072. delete series[prop];
  34073. });
  34074. let casting = false;
  34075. if (seriesTypes[newType]) {
  34076. casting = newType !== series.type;
  34077. // Destroy the series and delete all properties, it will be
  34078. // reinserted within the `init` call below
  34079. series.remove(false, false, false, true);
  34080. if (casting) {
  34081. // Modern browsers including IE11
  34082. if (Object.setPrototypeOf) {
  34083. Object.setPrototypeOf(series, seriesTypes[newType].prototype);
  34084. // Legacy (IE < 11)
  34085. }
  34086. else {
  34087. const ownEvents = Object.hasOwnProperty.call(series, 'hcEvents') && series.hcEvents;
  34088. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  34089. series[n] = void 0;
  34090. }
  34091. // Reinsert all methods and properties from the new type
  34092. // prototype (#2270, #3719).
  34093. extend(series, seriesTypes[newType].prototype);
  34094. // The events are tied to the prototype chain, don't copy if
  34095. // they're not the series' own
  34096. if (ownEvents) {
  34097. series.hcEvents = ownEvents;
  34098. }
  34099. else {
  34100. delete series.hcEvents;
  34101. }
  34102. }
  34103. }
  34104. }
  34105. else {
  34106. error(17, true, chart, { missingModuleFor: newType });
  34107. }
  34108. // Re-register groups (#3094) and other preserved properties
  34109. preserve.forEach(function (prop) {
  34110. series[prop] = preserve[prop];
  34111. });
  34112. series.init(chart, options);
  34113. // Remove particular elements of the points. Check `series.options`
  34114. // because we need to consider the options being set on plotOptions as
  34115. // well.
  34116. if (keepPoints && this.points) {
  34117. seriesOptions = series.options;
  34118. // What kind of elements to destroy
  34119. if (seriesOptions.visible === false) {
  34120. kinds.graphic = 1;
  34121. kinds.dataLabel = 1;
  34122. }
  34123. else if (!series._hasPointLabels) {
  34124. const { marker, dataLabels } = seriesOptions, oldMarker = oldOptions.marker || {};
  34125. // If the marker got disabled or changed its symbol, width or
  34126. // height - destroy
  34127. if (marker && (marker.enabled === false ||
  34128. oldMarker.symbol !== marker.symbol || // #10870, #15946
  34129. oldMarker.height !== marker.height || // #16274
  34130. oldMarker.width !== marker.width // #16274
  34131. )) {
  34132. kinds.graphic = 1;
  34133. }
  34134. if (dataLabels &&
  34135. dataLabels.enabled === false) {
  34136. kinds.dataLabel = 1;
  34137. }
  34138. }
  34139. for (const point of this.points) {
  34140. if (point && point.series) {
  34141. point.resolveColor();
  34142. // Destroy elements in order to recreate based on updated
  34143. // series options.
  34144. if (Object.keys(kinds).length) {
  34145. point.destroyElements(kinds);
  34146. }
  34147. if (seriesOptions.showInLegend === false &&
  34148. point.legendItem) {
  34149. chart.legend.destroyItem(point);
  34150. }
  34151. }
  34152. }
  34153. }
  34154. series.initialType = initialType;
  34155. chart.linkSeries(); // Links are lost in series.remove (#3028)
  34156. // #15383: Fire updatedData if the type has changed to keep linked
  34157. // series such as indicators updated
  34158. if (casting && series.linkedSeries.length) {
  34159. series.isDirtyData = true;
  34160. }
  34161. fireEvent(this, 'afterUpdate');
  34162. if (pick(redraw, true)) {
  34163. chart.redraw(keepPoints ? void 0 : false);
  34164. }
  34165. }
  34166. /**
  34167. * Used from within series.update
  34168. * @private
  34169. */
  34170. setName(name) {
  34171. this.name = this.options.name = this.userOptions.name = name;
  34172. this.chart.isDirtyLegend = true;
  34173. }
  34174. /**
  34175. * Check if the option has changed.
  34176. * @private
  34177. */
  34178. hasOptionChanged(optionName) {
  34179. const chart = this.chart, option = this.options[optionName], plotOptions = chart.options.plotOptions, oldOption = this.userOptions[optionName];
  34180. if (oldOption) {
  34181. return option !== oldOption;
  34182. }
  34183. return option !==
  34184. pick(plotOptions &&
  34185. plotOptions[this.type] &&
  34186. plotOptions[this.type][optionName], plotOptions &&
  34187. plotOptions.series &&
  34188. plotOptions.series[optionName], option);
  34189. }
  34190. /**
  34191. * Runs on mouse over the series graphical items.
  34192. *
  34193. * @function Highcharts.Series#onMouseOver
  34194. * @emits Highcharts.Series#event:mouseOver
  34195. */
  34196. onMouseOver() {
  34197. const series = this, chart = series.chart, hoverSeries = chart.hoverSeries, pointer = chart.pointer;
  34198. pointer.setHoverChartIndex();
  34199. // set normal state to previous series
  34200. if (hoverSeries && hoverSeries !== series) {
  34201. hoverSeries.onMouseOut();
  34202. }
  34203. // trigger the event, but to save processing time,
  34204. // only if defined
  34205. if (series.options.events.mouseOver) {
  34206. fireEvent(series, 'mouseOver');
  34207. }
  34208. // hover this
  34209. series.setState('hover');
  34210. /**
  34211. * Contains the original hovered series.
  34212. *
  34213. * @name Highcharts.Chart#hoverSeries
  34214. * @type {Highcharts.Series|null}
  34215. */
  34216. chart.hoverSeries = series;
  34217. }
  34218. /**
  34219. * Runs on mouse out of the series graphical items.
  34220. *
  34221. * @function Highcharts.Series#onMouseOut
  34222. *
  34223. * @emits Highcharts.Series#event:mouseOut
  34224. */
  34225. onMouseOut() {
  34226. // trigger the event only if listeners exist
  34227. const series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
  34228. // #182, set to null before the mouseOut event fires
  34229. chart.hoverSeries = null;
  34230. // trigger mouse out on the point, which must be in this series
  34231. if (hoverPoint) {
  34232. hoverPoint.onMouseOut();
  34233. }
  34234. // fire the mouse out event
  34235. if (series && options.events.mouseOut) {
  34236. fireEvent(series, 'mouseOut');
  34237. }
  34238. // hide the tooltip
  34239. if (tooltip &&
  34240. !series.stickyTracking &&
  34241. (!tooltip.shared || series.noSharedTooltip)) {
  34242. tooltip.hide();
  34243. }
  34244. // Reset all inactive states
  34245. chart.series.forEach(function (s) {
  34246. s.setState('', true);
  34247. });
  34248. }
  34249. /**
  34250. * Set the state of the series. Called internally on mouse interaction
  34251. * operations, but it can also be called directly to visually
  34252. * highlight a series.
  34253. *
  34254. * @function Highcharts.Series#setState
  34255. *
  34256. * @param {Highcharts.SeriesStateValue|""} [state]
  34257. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  34258. * or `''` (an empty string), `'normal'` or `undefined` to set to
  34259. * normal state.
  34260. * @param {boolean} [inherit]
  34261. * Determines if state should be inherited by points too.
  34262. */
  34263. setState(state, inherit) {
  34264. const series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states,
  34265. // By default a quick animation to hover/inactive,
  34266. // slower to un-hover
  34267. stateAnimation = pick((stateOptions[state || 'normal'] &&
  34268. stateOptions[state || 'normal'].animation), series.chart.options.chart.animation);
  34269. let attribs, lineWidth = options.lineWidth, i = 0, opacity = options.opacity;
  34270. state = state || '';
  34271. if (series.state !== state) {
  34272. // Toggle class names
  34273. [
  34274. series.group,
  34275. series.markerGroup,
  34276. series.dataLabelsGroup
  34277. ].forEach(function (group) {
  34278. if (group) {
  34279. // Old state
  34280. if (series.state) {
  34281. group.removeClass('highcharts-series-' + series.state);
  34282. }
  34283. // New state
  34284. if (state) {
  34285. group.addClass('highcharts-series-' + state);
  34286. }
  34287. }
  34288. });
  34289. series.state = state;
  34290. if (!series.chart.styledMode) {
  34291. if (stateOptions[state] &&
  34292. stateOptions[state].enabled === false) {
  34293. return;
  34294. }
  34295. if (state) {
  34296. lineWidth = (stateOptions[state].lineWidth ||
  34297. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  34298. opacity = pick(stateOptions[state].opacity, opacity);
  34299. }
  34300. if (graph && !graph.dashstyle && isNumber(lineWidth)) {
  34301. attribs = {
  34302. 'stroke-width': lineWidth
  34303. };
  34304. // Animate the graph stroke-width.
  34305. graph.animate(attribs, stateAnimation);
  34306. while (series['zone-graph-' + i]) {
  34307. series['zone-graph-' + i].animate(attribs, stateAnimation);
  34308. i = i + 1;
  34309. }
  34310. }
  34311. // For some types (pie, networkgraph, sankey) opacity is
  34312. // resolved on a point level
  34313. if (!inactiveOtherPoints) {
  34314. [
  34315. series.group,
  34316. series.markerGroup,
  34317. series.dataLabelsGroup,
  34318. series.labelBySeries
  34319. ].forEach(function (group) {
  34320. if (group) {
  34321. group.animate({
  34322. opacity: opacity
  34323. }, stateAnimation);
  34324. }
  34325. });
  34326. }
  34327. }
  34328. }
  34329. // Don't loop over points on a series that doesn't apply inactive state
  34330. // to siblings markers (e.g. line, column)
  34331. if (inherit && inactiveOtherPoints && series.points) {
  34332. series.setAllPointsToState(state || void 0);
  34333. }
  34334. }
  34335. /**
  34336. * Set the state for all points in the series.
  34337. *
  34338. * @function Highcharts.Series#setAllPointsToState
  34339. *
  34340. * @private
  34341. *
  34342. * @param {string} [state]
  34343. * Can be either `hover` or undefined to set to normal state.
  34344. */
  34345. setAllPointsToState(state) {
  34346. this.points.forEach(function (point) {
  34347. if (point.setState) {
  34348. point.setState(state);
  34349. }
  34350. });
  34351. }
  34352. /**
  34353. * Show or hide the series.
  34354. *
  34355. * @function Highcharts.Series#setVisible
  34356. *
  34357. * @param {boolean} [visible]
  34358. * True to show the series, false to hide. If undefined, the visibility is
  34359. * toggled.
  34360. *
  34361. * @param {boolean} [redraw=true]
  34362. * Whether to redraw the chart after the series is altered. If doing more
  34363. * operations on the chart, it is a good idea to set redraw to false and
  34364. * call {@link Chart#redraw|chart.redraw()} after.
  34365. *
  34366. * @emits Highcharts.Series#event:hide
  34367. * @emits Highcharts.Series#event:show
  34368. */
  34369. setVisible(vis, redraw) {
  34370. const series = this, chart = series.chart, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
  34371. // if called without an argument, toggle visibility
  34372. series.visible =
  34373. vis =
  34374. series.options.visible =
  34375. series.userOptions.visible =
  34376. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  34377. const showOrHide = vis ? 'show' : 'hide';
  34378. // show or hide elements
  34379. [
  34380. 'group',
  34381. 'dataLabelsGroup',
  34382. 'markerGroup',
  34383. 'tracker',
  34384. 'tt'
  34385. ].forEach(function (key) {
  34386. if (series[key]) {
  34387. series[key][showOrHide]();
  34388. }
  34389. });
  34390. // hide tooltip (#1361)
  34391. if (chart.hoverSeries === series ||
  34392. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  34393. series.onMouseOut();
  34394. }
  34395. if (series.legendItem) {
  34396. chart.legend.colorizeItem(series, vis);
  34397. }
  34398. // rescale or adapt to resized chart
  34399. series.isDirty = true;
  34400. // in a stack, all other series are affected
  34401. if (series.options.stacking) {
  34402. chart.series.forEach(function (otherSeries) {
  34403. if (otherSeries.options.stacking && otherSeries.visible) {
  34404. otherSeries.isDirty = true;
  34405. }
  34406. });
  34407. }
  34408. // show or hide linked series
  34409. series.linkedSeries.forEach(function (otherSeries) {
  34410. otherSeries.setVisible(vis, false);
  34411. });
  34412. if (ignoreHiddenSeries) {
  34413. chart.isDirtyBox = true;
  34414. }
  34415. fireEvent(series, showOrHide);
  34416. if (redraw !== false) {
  34417. chart.redraw();
  34418. }
  34419. }
  34420. /**
  34421. * Show the series if hidden.
  34422. *
  34423. * @sample highcharts/members/series-hide/
  34424. * Toggle visibility from a button
  34425. *
  34426. * @function Highcharts.Series#show
  34427. * @emits Highcharts.Series#event:show
  34428. */
  34429. show() {
  34430. this.setVisible(true);
  34431. }
  34432. /**
  34433. * Hide the series if visible. If the
  34434. * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
  34435. * option is true, the chart is redrawn without this series.
  34436. *
  34437. * @sample highcharts/members/series-hide/
  34438. * Toggle visibility from a button
  34439. *
  34440. * @function Highcharts.Series#hide
  34441. * @emits Highcharts.Series#event:hide
  34442. */
  34443. hide() {
  34444. this.setVisible(false);
  34445. }
  34446. /**
  34447. * Select or unselect the series. This means its
  34448. * {@link Highcharts.Series.selected|selected}
  34449. * property is set, the checkbox in the legend is toggled and when selected,
  34450. * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
  34451. * function.
  34452. *
  34453. * @sample highcharts/members/series-select/
  34454. * Select a series from a button
  34455. *
  34456. * @function Highcharts.Series#select
  34457. *
  34458. * @param {boolean} [selected]
  34459. * True to select the series, false to unselect. If undefined, the selection
  34460. * state is toggled.
  34461. *
  34462. * @emits Highcharts.Series#event:select
  34463. * @emits Highcharts.Series#event:unselect
  34464. */
  34465. select(selected) {
  34466. const series = this;
  34467. series.selected =
  34468. selected =
  34469. this.options.selected = (typeof selected === 'undefined' ?
  34470. !series.selected :
  34471. selected);
  34472. if (series.checkbox) {
  34473. series.checkbox.checked = selected;
  34474. }
  34475. fireEvent(series, selected ? 'select' : 'unselect');
  34476. }
  34477. /**
  34478. * Checks if a tooltip should be shown for a given point.
  34479. *
  34480. * @private
  34481. */
  34482. shouldShowTooltip(plotX, plotY, options = {}) {
  34483. options.series = this;
  34484. options.visiblePlotOnly = true;
  34485. return this.chart.isInsidePlot(plotX, plotY, options);
  34486. }
  34487. /**
  34488. * Draws the legend symbol based on the legendSymbol user option.
  34489. *
  34490. * @private
  34491. */
  34492. drawLegendSymbol(legend, item) {
  34493. var _a;
  34494. (_a = LegendSymbol[this.options.legendSymbol || 'rectangle']) === null || _a === void 0 ? void 0 : _a.call(this, legend, item);
  34495. }
  34496. }
  34497. Series.defaultOptions = SeriesDefaults;
  34498. /**
  34499. * Registry of all available series types.
  34500. *
  34501. * @name Highcharts.Series.types
  34502. * @type {Highcharts.Dictionary<typeof_Highcharts.Series>}
  34503. */
  34504. Series.types = SeriesRegistry.seriesTypes;
  34505. /* *
  34506. *
  34507. * Static Functions
  34508. *
  34509. * */
  34510. /**
  34511. * Registers a series class to be accessible via `Series.types`.
  34512. *
  34513. * @function Highcharts.Series.registerType
  34514. *
  34515. * @param {string} seriesType
  34516. * The series type as an identifier string in lower case.
  34517. *
  34518. * @param {Function} SeriesClass
  34519. * The series class as a class pattern or a constructor function with
  34520. * prototype.
  34521. */
  34522. Series.registerType = SeriesRegistry.registerSeriesType;
  34523. extend(Series.prototype, {
  34524. axisTypes: ['xAxis', 'yAxis'],
  34525. coll: 'series',
  34526. colorCounter: 0,
  34527. cropShoulder: 1,
  34528. directTouch: false,
  34529. isCartesian: true,
  34530. kdAxisArray: ['clientX', 'plotY'],
  34531. // each point's x and y values are stored in this.xData and this.yData:
  34532. parallelArrays: ['x', 'y'],
  34533. pointClass: Point,
  34534. requireSorting: true,
  34535. // requires the data to be sorted:
  34536. sorted: true
  34537. });
  34538. /* *
  34539. *
  34540. * Registry
  34541. *
  34542. * */
  34543. SeriesRegistry.series = Series;
  34544. /* *
  34545. *
  34546. * Default Export
  34547. *
  34548. * */
  34549. /* *
  34550. *
  34551. * API Declarations
  34552. *
  34553. * */
  34554. /**
  34555. * This is a placeholder type of the possible series options for
  34556. * [Highcharts](../highcharts/series), [Highcharts Stock](../highstock/series),
  34557. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  34558. *
  34559. * In TypeScript is this dynamically generated to reference all possible types
  34560. * of series options.
  34561. *
  34562. * @ignore-declaration
  34563. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  34564. */
  34565. /**
  34566. * Options for `dataSorting`.
  34567. *
  34568. * @interface Highcharts.DataSortingOptionsObject
  34569. * @since 8.0.0
  34570. */ /**
  34571. * Enable or disable data sorting for the series.
  34572. * @name Highcharts.DataSortingOptionsObject#enabled
  34573. * @type {boolean|undefined}
  34574. */ /**
  34575. * Whether to allow matching points by name in an update.
  34576. * @name Highcharts.DataSortingOptionsObject#matchByName
  34577. * @type {boolean|undefined}
  34578. */ /**
  34579. * Determines what data value should be used to sort by.
  34580. * @name Highcharts.DataSortingOptionsObject#sortKey
  34581. * @type {string|undefined}
  34582. */
  34583. /**
  34584. * Function callback when a series has been animated.
  34585. *
  34586. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  34587. *
  34588. * @param {Highcharts.Series} this
  34589. * The series where the event occured.
  34590. *
  34591. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  34592. * Event arguments.
  34593. */
  34594. /**
  34595. * Event information regarding completed animation of a series.
  34596. *
  34597. * @interface Highcharts.SeriesAfterAnimateEventObject
  34598. */ /**
  34599. * Animated series.
  34600. * @name Highcharts.SeriesAfterAnimateEventObject#target
  34601. * @type {Highcharts.Series}
  34602. */ /**
  34603. * Event type.
  34604. * @name Highcharts.SeriesAfterAnimateEventObject#type
  34605. * @type {"afterAnimate"}
  34606. */
  34607. /**
  34608. * Function callback when the checkbox next to the series' name in the legend is
  34609. * clicked.
  34610. *
  34611. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  34612. *
  34613. * @param {Highcharts.Series} this
  34614. * The series where the event occured.
  34615. *
  34616. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  34617. * Event arguments.
  34618. */
  34619. /**
  34620. * Event information regarding check of a series box.
  34621. *
  34622. * @interface Highcharts.SeriesCheckboxClickEventObject
  34623. */ /**
  34624. * Whether the box has been checked.
  34625. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  34626. * @type {boolean}
  34627. */ /**
  34628. * Related series.
  34629. * @name Highcharts.SeriesCheckboxClickEventObject#item
  34630. * @type {Highcharts.Series}
  34631. */ /**
  34632. * Related series.
  34633. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34634. * @type {Highcharts.Series}
  34635. */ /**
  34636. * Event type.
  34637. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34638. * @type {"checkboxClick"}
  34639. */
  34640. /**
  34641. * Function callback when a series is clicked. Return false to cancel toogle
  34642. * actions.
  34643. *
  34644. * @callback Highcharts.SeriesClickCallbackFunction
  34645. *
  34646. * @param {Highcharts.Series} this
  34647. * The series where the event occured.
  34648. *
  34649. * @param {Highcharts.SeriesClickEventObject} event
  34650. * Event arguments.
  34651. */
  34652. /**
  34653. * Common information for a click event on a series.
  34654. *
  34655. * @interface Highcharts.SeriesClickEventObject
  34656. * @extends global.Event
  34657. */ /**
  34658. * Nearest point on the graph.
  34659. * @name Highcharts.SeriesClickEventObject#point
  34660. * @type {Highcharts.Point}
  34661. */
  34662. /**
  34663. * Gets fired when the series is hidden after chart generation time, either by
  34664. * clicking the legend item or by calling `.hide()`.
  34665. *
  34666. * @callback Highcharts.SeriesHideCallbackFunction
  34667. *
  34668. * @param {Highcharts.Series} this
  34669. * The series where the event occured.
  34670. *
  34671. * @param {global.Event} event
  34672. * The event that occured.
  34673. */
  34674. /**
  34675. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  34676. * graph.
  34677. *
  34678. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  34679. */
  34680. /**
  34681. * Gets fired when the legend item belonging to the series is clicked. The
  34682. * default action is to toggle the visibility of the series. This can be
  34683. * prevented by returning `false` or calling `event.preventDefault()`.
  34684. *
  34685. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  34686. *
  34687. * @param {Highcharts.Series} this
  34688. * The series where the event occured.
  34689. *
  34690. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  34691. * The event that occured.
  34692. */
  34693. /**
  34694. * Information about the event.
  34695. *
  34696. * @interface Highcharts.SeriesLegendItemClickEventObject
  34697. */ /**
  34698. * Related browser event.
  34699. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  34700. * @type {global.PointerEvent}
  34701. */ /**
  34702. * Prevent the default action of toggle the visibility of the series.
  34703. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  34704. * @type {Function}
  34705. */ /**
  34706. * Related series.
  34707. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34708. * @type {Highcharts.Series}
  34709. */ /**
  34710. * Event type.
  34711. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34712. * @type {"checkboxClick"}
  34713. */
  34714. /**
  34715. * Gets fired when the mouse leaves the graph.
  34716. *
  34717. * @callback Highcharts.SeriesMouseOutCallbackFunction
  34718. *
  34719. * @param {Highcharts.Series} this
  34720. * Series where the event occured.
  34721. *
  34722. * @param {global.PointerEvent} event
  34723. * Event that occured.
  34724. */
  34725. /**
  34726. * Gets fired when the mouse enters the graph.
  34727. *
  34728. * @callback Highcharts.SeriesMouseOverCallbackFunction
  34729. *
  34730. * @param {Highcharts.Series} this
  34731. * Series where the event occured.
  34732. *
  34733. * @param {global.PointerEvent} event
  34734. * Event that occured.
  34735. */
  34736. /**
  34737. * Translation and scale for the plot area of a series.
  34738. *
  34739. * @interface Highcharts.SeriesPlotBoxObject
  34740. */ /**
  34741. * @name Highcharts.SeriesPlotBoxObject#scaleX
  34742. * @type {number}
  34743. */ /**
  34744. * @name Highcharts.SeriesPlotBoxObject#scaleY
  34745. * @type {number}
  34746. */ /**
  34747. * @name Highcharts.SeriesPlotBoxObject#translateX
  34748. * @type {number}
  34749. */ /**
  34750. * @name Highcharts.SeriesPlotBoxObject#translateY
  34751. * @type {number}
  34752. */
  34753. /**
  34754. * Gets fired when the series is shown after chart generation time, either by
  34755. * clicking the legend item or by calling `.show()`.
  34756. *
  34757. * @callback Highcharts.SeriesShowCallbackFunction
  34758. *
  34759. * @param {Highcharts.Series} this
  34760. * Series where the event occured.
  34761. *
  34762. * @param {global.Event} event
  34763. * Event that occured.
  34764. */
  34765. /**
  34766. * Possible key values for the series state options.
  34767. *
  34768. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  34769. */
  34770. ''; // detach doclets above
  34771. /* *
  34772. *
  34773. * API Options
  34774. *
  34775. * */
  34776. /**
  34777. * Series options for specific data and the data itself. In TypeScript you
  34778. * have to cast the series options to specific series types, to get all
  34779. * possible options for a series.
  34780. *
  34781. * @example
  34782. * // TypeScript example
  34783. * Highcharts.chart('container', {
  34784. * series: [{
  34785. * color: '#06C',
  34786. * data: [[0, 1], [2, 3]]
  34787. * } as Highcharts.SeriesLineOptions ]
  34788. * });
  34789. *
  34790. * @type {Array<*>}
  34791. * @apioption series
  34792. */
  34793. /**
  34794. * An id for the series. This can be used after render time to get a pointer
  34795. * to the series object through `chart.get()`.
  34796. *
  34797. * @sample {highcharts} highcharts/plotoptions/series-id/
  34798. * Get series by id
  34799. *
  34800. * @type {string}
  34801. * @since 1.2.0
  34802. * @apioption series.id
  34803. */
  34804. /**
  34805. * The index of the series in the chart, affecting the internal index in the
  34806. * `chart.series` array, the visible Z index as well as the order in the
  34807. * legend.
  34808. *
  34809. * @type {number}
  34810. * @since 2.3.0
  34811. * @apioption series.index
  34812. */
  34813. /**
  34814. * The sequential index of the series in the legend.
  34815. *
  34816. * @see [legend.reversed](#legend.reversed),
  34817. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  34818. *
  34819. * @sample {highcharts|highstock} highcharts/series/legendindex/
  34820. * Legend in opposite order
  34821. *
  34822. * @type {number}
  34823. * @apioption series.legendIndex
  34824. */
  34825. /**
  34826. * The name of the series as shown in the legend, tooltip etc.
  34827. *
  34828. * @sample {highcharts} highcharts/series/name/
  34829. * Series name
  34830. * @sample {highmaps} maps/demo/category-map/
  34831. * Series name
  34832. *
  34833. * @type {string}
  34834. * @apioption series.name
  34835. */
  34836. /**
  34837. * This option allows grouping series in a stacked chart. The stack option
  34838. * can be a string or anything else, as long as the grouped series' stack
  34839. * options match each other after conversion into a string.
  34840. *
  34841. * @sample {highcharts} highcharts/series/stack/
  34842. * Stacked and grouped columns
  34843. *
  34844. * @type {number|string}
  34845. * @since 2.1
  34846. * @product highcharts highstock
  34847. * @apioption series.stack
  34848. */
  34849. /**
  34850. * The type of series, for example `line` or `column`. By default, the
  34851. * series type is inherited from [chart.type](#chart.type), so unless the
  34852. * chart is a combination of series types, there is no need to set it on the
  34853. * series level.
  34854. *
  34855. * @sample {highcharts} highcharts/series/type/
  34856. * Line and column in the same chart
  34857. * @sample highcharts/series/type-dynamic/
  34858. * Dynamic types with button selector
  34859. * @sample {highmaps} maps/demo/mapline-mappoint/
  34860. * Multiple types in the same map
  34861. *
  34862. * @type {string}
  34863. * @apioption series.type
  34864. */
  34865. /**
  34866. * When using dual or multiple x axes, this number defines which xAxis the
  34867. * particular series is connected to. It refers to either the
  34868. * {@link #xAxis.id|axis id}
  34869. * or the index of the axis in the xAxis array, with 0 being the first.
  34870. *
  34871. * @type {number|string}
  34872. * @default 0
  34873. * @product highcharts highstock
  34874. * @apioption series.xAxis
  34875. */
  34876. /**
  34877. * When using dual or multiple y axes, this number defines which yAxis the
  34878. * particular series is connected to. It refers to either the
  34879. * {@link #yAxis.id|axis id}
  34880. * or the index of the axis in the yAxis array, with 0 being the first.
  34881. *
  34882. * @sample {highcharts} highcharts/series/yaxis/
  34883. * Apply the column series to the secondary Y axis
  34884. *
  34885. * @type {number|string}
  34886. * @default 0
  34887. * @product highcharts highstock
  34888. * @apioption series.yAxis
  34889. */
  34890. /**
  34891. * Define the visual z index of the series.
  34892. *
  34893. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  34894. * With no z index, the series defined last are on top
  34895. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  34896. * With a z index, the series with the highest z index is on top
  34897. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  34898. * With no z index, the series defined last are on top
  34899. * @sample {highstock} highcharts/plotoptions/series-zindex/
  34900. * With a z index, the series with the highest z index is on top
  34901. *
  34902. * @type {number}
  34903. * @product highcharts highstock
  34904. * @apioption series.zIndex
  34905. */
  34906. ''; // include precedent doclets in transpilat
  34907. return Series;
  34908. });
  34909. _registerModule(_modules, 'Core/Chart/Chart.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Templating.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js'], _modules['Core/Renderer/HTML/AST.js']], function (A, Axis, D, Templating, Foundation, H, RendererRegistry, Series, SeriesRegistry, SVGRenderer, Time, U, AST) {
  34910. /* *
  34911. *
  34912. * (c) 2010-2021 Torstein Honsi
  34913. *
  34914. * License: www.highcharts.com/license
  34915. *
  34916. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34917. *
  34918. * */
  34919. const { animate, animObject, setAnimation } = A;
  34920. const { defaultOptions, defaultTime } = D;
  34921. const { numberFormat } = Templating;
  34922. const { registerEventOptions } = Foundation;
  34923. const { charts, doc, marginNames, svg, win } = H;
  34924. const { seriesTypes } = SeriesRegistry;
  34925. const { addEvent, attr, createElement, css, defined, diffObjects, discardElement, erase, error, extend, find, fireEvent, getStyle, isArray, isNumber, isObject, isString, merge, objectEach, pick, pInt, relativeLength, removeEvent, splat, syncTimeout, uniqueKey } = U;
  34926. /* *
  34927. *
  34928. * Class
  34929. *
  34930. * */
  34931. /* eslint-disable no-invalid-this, valid-jsdoc */
  34932. /**
  34933. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  34934. *
  34935. * @example
  34936. * let chart = Highcharts.chart('container', {
  34937. * title: {
  34938. * text: 'My chart'
  34939. * },
  34940. * series: [{
  34941. * data: [1, 3, 2, 4]
  34942. * }]
  34943. * })
  34944. *
  34945. * @class
  34946. * @name Highcharts.Chart
  34947. *
  34948. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34949. * The DOM element to render to, or its id.
  34950. *
  34951. * @param {Highcharts.Options} options
  34952. * The chart options structure.
  34953. *
  34954. * @param {Highcharts.ChartCallbackFunction} [callback]
  34955. * Function to run when the chart has loaded and and all external images
  34956. * are loaded. Defining a
  34957. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34958. * handler is equivalent.
  34959. */
  34960. class Chart {
  34961. /**
  34962. * Factory function for basic charts.
  34963. *
  34964. * @example
  34965. * // Render a chart in to div#container
  34966. * let chart = Highcharts.chart('container', {
  34967. * title: {
  34968. * text: 'My chart'
  34969. * },
  34970. * series: [{
  34971. * data: [1, 3, 2, 4]
  34972. * }]
  34973. * });
  34974. *
  34975. * @function Highcharts.chart
  34976. *
  34977. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34978. * The DOM element to render to, or its id.
  34979. *
  34980. * @param {Highcharts.Options} options
  34981. * The chart options structure.
  34982. *
  34983. * @param {Highcharts.ChartCallbackFunction} [callback]
  34984. * Function to run when the chart has loaded and and all external images are
  34985. * loaded. Defining a
  34986. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34987. * handler is equivalent.
  34988. *
  34989. * @return {Highcharts.Chart}
  34990. * Returns the Chart object.
  34991. */
  34992. static chart(a, b, c) {
  34993. return new Chart(a, b, c);
  34994. }
  34995. constructor(a, b, c) {
  34996. this.axes = void 0;
  34997. this.axisOffset = void 0;
  34998. this.bounds = void 0;
  34999. this.chartHeight = void 0;
  35000. this.chartWidth = void 0;
  35001. this.clipBox = void 0;
  35002. this.colorCounter = void 0;
  35003. this.container = void 0;
  35004. this.eventOptions = void 0;
  35005. this.index = void 0;
  35006. this.isResizing = void 0;
  35007. this.labelCollectors = void 0;
  35008. this.margin = void 0;
  35009. this.numberFormatter = void 0;
  35010. this.options = void 0;
  35011. this.plotBox = void 0;
  35012. this.plotHeight = void 0;
  35013. this.plotLeft = void 0;
  35014. this.plotTop = void 0;
  35015. this.plotWidth = void 0;
  35016. this.pointCount = void 0;
  35017. this.pointer = void 0;
  35018. this.renderer = void 0;
  35019. this.renderTo = void 0;
  35020. this.series = void 0;
  35021. this.sharedClips = {};
  35022. this.spacing = void 0;
  35023. this.spacingBox = void 0;
  35024. this.symbolCounter = void 0;
  35025. this.time = void 0;
  35026. this.titleOffset = void 0;
  35027. this.userOptions = void 0;
  35028. this.xAxis = void 0;
  35029. this.yAxis = void 0;
  35030. this.zooming = void 0;
  35031. this.getArgs(a, b, c);
  35032. }
  35033. /* *
  35034. *
  35035. * Functions
  35036. *
  35037. * */
  35038. /**
  35039. * Handle the arguments passed to the constructor.
  35040. *
  35041. * @private
  35042. * @function Highcharts.Chart#getArgs
  35043. *
  35044. * @param {...Array<*>} arguments
  35045. * All arguments for the constructor.
  35046. *
  35047. * @emits Highcharts.Chart#event:init
  35048. * @emits Highcharts.Chart#event:afterInit
  35049. */
  35050. getArgs(a, b, c) {
  35051. // Remove the optional first argument, renderTo, and
  35052. // set it on this.
  35053. if (isString(a) || a.nodeName) {
  35054. this.renderTo = a;
  35055. this.init(b, c);
  35056. }
  35057. else {
  35058. this.init(a, b);
  35059. }
  35060. }
  35061. /**
  35062. * Function setting zoom options after chart init and after chart update.
  35063. * Offers support for deprecated options.
  35064. *
  35065. * @private
  35066. * @function Highcharts.Chart#setZoomOptions
  35067. */
  35068. setZoomOptions() {
  35069. const chart = this, options = chart.options.chart, zooming = options.zooming;
  35070. chart.zooming = Object.assign(Object.assign({}, zooming), { type: pick(options.zoomType, zooming.type), key: pick(options.zoomKey, zooming.key), pinchType: pick(options.pinchType, zooming.pinchType), singleTouch: pick(options.zoomBySingleTouch, zooming.singleTouch, false), resetButton: merge(zooming.resetButton, options.resetZoomButton) });
  35071. }
  35072. /**
  35073. * Overridable function that initializes the chart. The constructor's
  35074. * arguments are passed on directly.
  35075. *
  35076. * @function Highcharts.Chart#init
  35077. *
  35078. * @param {Highcharts.Options} userOptions
  35079. * Custom options.
  35080. *
  35081. * @param {Function} [callback]
  35082. * Function to run when the chart has loaded and and all external
  35083. * images are loaded.
  35084. *
  35085. *
  35086. * @emits Highcharts.Chart#event:init
  35087. * @emits Highcharts.Chart#event:afterInit
  35088. */
  35089. init(userOptions, callback) {
  35090. // Fire the event with a default function
  35091. fireEvent(this, 'init', { args: arguments }, function () {
  35092. const options = merge(defaultOptions, userOptions), // do the merge
  35093. optionsChart = options.chart;
  35094. /**
  35095. * The original options given to the constructor or a chart factory
  35096. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  35097. * The original options are shallow copied to avoid mutation. The
  35098. * copy, `chart.userOptions`, may later be mutated to reflect
  35099. * updated options throughout the lifetime of the chart.
  35100. *
  35101. * For collections, like `series`, `xAxis` and `yAxis`, the chart
  35102. * user options should always be reflected by the item user option,
  35103. * so for example the following should always be true:
  35104. *
  35105. * `chart.xAxis[0].userOptions === chart.userOptions.xAxis[0]`
  35106. *
  35107. * @name Highcharts.Chart#userOptions
  35108. * @type {Highcharts.Options}
  35109. */
  35110. this.userOptions = extend({}, userOptions);
  35111. this.margin = [];
  35112. this.spacing = [];
  35113. // Pixel data bounds for touch zoom
  35114. this.bounds = { h: {}, v: {} };
  35115. // An array of functions that returns labels that should be
  35116. // considered for anti-collision
  35117. this.labelCollectors = [];
  35118. this.callback = callback;
  35119. this.isResizing = 0;
  35120. /**
  35121. * The options structure for the chart after merging
  35122. * {@link #defaultOptions} and {@link #userOptions}. It contains
  35123. * members for the sub elements like series, legend, tooltip etc.
  35124. *
  35125. * @name Highcharts.Chart#options
  35126. * @type {Highcharts.Options}
  35127. */
  35128. this.options = options;
  35129. /**
  35130. * All the axes in the chart.
  35131. *
  35132. * @see Highcharts.Chart.xAxis
  35133. * @see Highcharts.Chart.yAxis
  35134. *
  35135. * @name Highcharts.Chart#axes
  35136. * @type {Array<Highcharts.Axis>}
  35137. */
  35138. this.axes = [];
  35139. /**
  35140. * All the current series in the chart.
  35141. *
  35142. * @name Highcharts.Chart#series
  35143. * @type {Array<Highcharts.Series>}
  35144. */
  35145. this.series = [];
  35146. /**
  35147. * The `Time` object associated with the chart. Since v6.0.5,
  35148. * time settings can be applied individually for each chart. If
  35149. * no individual settings apply, the `Time` object is shared by
  35150. * all instances.
  35151. *
  35152. * @name Highcharts.Chart#time
  35153. * @type {Highcharts.Time}
  35154. */
  35155. this.time =
  35156. userOptions.time && Object.keys(userOptions.time).length ?
  35157. new Time(userOptions.time) :
  35158. H.time;
  35159. /**
  35160. * Callback function to override the default function that formats
  35161. * all the numbers in the chart. Returns a string with the formatted
  35162. * number.
  35163. *
  35164. * @name Highcharts.Chart#numberFormatter
  35165. * @type {Highcharts.NumberFormatterCallbackFunction}
  35166. */
  35167. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  35168. /**
  35169. * Whether the chart is in styled mode, meaning all presentational
  35170. * attributes are avoided.
  35171. *
  35172. * @name Highcharts.Chart#styledMode
  35173. * @type {boolean}
  35174. */
  35175. this.styledMode = optionsChart.styledMode;
  35176. this.hasCartesianSeries = optionsChart.showAxes;
  35177. const chart = this;
  35178. /**
  35179. * Index position of the chart in the {@link Highcharts#charts}
  35180. * property.
  35181. *
  35182. * @name Highcharts.Chart#index
  35183. * @type {number}
  35184. * @readonly
  35185. */
  35186. chart.index = charts.length; // Add the chart to the global lookup
  35187. charts.push(chart);
  35188. H.chartCount++;
  35189. // Chart event handlers
  35190. registerEventOptions(this, optionsChart);
  35191. /**
  35192. * A collection of the X axes in the chart.
  35193. *
  35194. * @name Highcharts.Chart#xAxis
  35195. * @type {Array<Highcharts.Axis>}
  35196. */
  35197. chart.xAxis = [];
  35198. /**
  35199. * A collection of the Y axes in the chart.
  35200. *
  35201. * @name Highcharts.Chart#yAxis
  35202. * @type {Array<Highcharts.Axis>}
  35203. *
  35204. * @todo
  35205. * Make events official: Fire the event `afterInit`.
  35206. */
  35207. chart.yAxis = [];
  35208. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  35209. this.setZoomOptions();
  35210. // Fire after init but before first render, before axes and series
  35211. // have been initialized.
  35212. fireEvent(chart, 'afterInit');
  35213. chart.firstRender();
  35214. });
  35215. }
  35216. /**
  35217. * Internal function to unitialize an individual series.
  35218. *
  35219. * @private
  35220. * @function Highcharts.Chart#initSeries
  35221. */
  35222. initSeries(options) {
  35223. const chart = this, optionsChart = chart.options.chart, type = (options.type ||
  35224. optionsChart.type), SeriesClass = seriesTypes[type];
  35225. // No such series type
  35226. if (!SeriesClass) {
  35227. error(17, true, chart, { missingModuleFor: type });
  35228. }
  35229. const series = new SeriesClass();
  35230. if (typeof series.init === 'function') {
  35231. series.init(chart, options);
  35232. }
  35233. return series;
  35234. }
  35235. /**
  35236. * Internal function to set data for all series with enabled sorting.
  35237. *
  35238. * @private
  35239. * @function Highcharts.Chart#setSeriesData
  35240. */
  35241. setSeriesData() {
  35242. this.getSeriesOrderByLinks().forEach(function (series) {
  35243. // We need to set data for series with sorting after series init
  35244. if (!series.points && !series.data && series.enabledDataSorting) {
  35245. series.setData(series.options.data, false);
  35246. }
  35247. });
  35248. }
  35249. /**
  35250. * Sort and return chart series in order depending on the number of linked
  35251. * series.
  35252. *
  35253. * @private
  35254. * @function Highcharts.Series#getSeriesOrderByLinks
  35255. */
  35256. getSeriesOrderByLinks() {
  35257. return this.series.concat().sort(function (a, b) {
  35258. if (a.linkedSeries.length || b.linkedSeries.length) {
  35259. return b.linkedSeries.length - a.linkedSeries.length;
  35260. }
  35261. return 0;
  35262. });
  35263. }
  35264. /**
  35265. * Order all series or axes above a given index. When series or axes are
  35266. * added and ordered by configuration, only the last series is handled
  35267. * (#248, #1123, #2456, #6112). This function is called on series and axis
  35268. * initialization and destroy.
  35269. *
  35270. * @private
  35271. * @function Highcharts.Chart#orderItems
  35272. * @param {string} coll The collection name
  35273. * @param {number} [fromIndex=0]
  35274. * If this is given, only the series above this index are handled.
  35275. */
  35276. orderItems(coll, fromIndex = 0) {
  35277. const collection = this[coll],
  35278. // Item options should be reflected in chart.options.series,
  35279. // chart.options.yAxis etc
  35280. optionsArray = this.options[coll] = splat(this.options[coll])
  35281. .slice(), userOptionsArray = this.userOptions[coll] = this.userOptions[coll] ?
  35282. splat(this.userOptions[coll]).slice() :
  35283. [];
  35284. if (this.hasRendered) {
  35285. // Remove all above index
  35286. optionsArray.splice(fromIndex);
  35287. userOptionsArray.splice(fromIndex);
  35288. }
  35289. if (collection) {
  35290. for (let i = fromIndex, iEnd = collection.length; i < iEnd; ++i) {
  35291. const item = collection[i];
  35292. if (item) {
  35293. /**
  35294. * Contains the series' index in the `Chart.series` array.
  35295. *
  35296. * @name Highcharts.Series#index
  35297. * @type {number}
  35298. * @readonly
  35299. */
  35300. item.index = i;
  35301. if (item instanceof Series) {
  35302. item.name = item.getName();
  35303. }
  35304. if (!item.options.isInternal) {
  35305. optionsArray[i] = item.options;
  35306. userOptionsArray[i] = item.userOptions;
  35307. }
  35308. }
  35309. }
  35310. }
  35311. }
  35312. /**
  35313. * Check whether a given point is within the plot area.
  35314. *
  35315. * @function Highcharts.Chart#isInsidePlot
  35316. *
  35317. * @param {number} plotX
  35318. * Pixel x relative to the plot area.
  35319. *
  35320. * @param {number} plotY
  35321. * Pixel y relative to the plot area.
  35322. *
  35323. * @param {Highcharts.ChartIsInsideOptionsObject} [options]
  35324. * Options object.
  35325. *
  35326. * @return {boolean}
  35327. * Returns true if the given point is inside the plot area.
  35328. */
  35329. isInsidePlot(plotX, plotY, options = {}) {
  35330. const { inverted, plotBox, plotLeft, plotTop, scrollablePlotBox } = this;
  35331. let scrollLeft = 0, scrollTop = 0;
  35332. if (options.visiblePlotOnly && this.scrollingContainer) {
  35333. ({ scrollLeft, scrollTop } = this.scrollingContainer);
  35334. }
  35335. const series = options.series, box = (options.visiblePlotOnly && scrollablePlotBox) || plotBox, x = options.inverted ? plotY : plotX, y = options.inverted ? plotX : plotY, e = {
  35336. x,
  35337. y,
  35338. isInsidePlot: true,
  35339. options
  35340. };
  35341. if (!options.ignoreX) {
  35342. const xAxis = (series &&
  35343. (inverted && !this.polar ? series.yAxis : series.xAxis)) || {
  35344. pos: plotLeft,
  35345. len: Infinity
  35346. };
  35347. const chartX = options.paneCoordinates ?
  35348. xAxis.pos + x : plotLeft + x;
  35349. if (!(chartX >= Math.max(scrollLeft + plotLeft, xAxis.pos) &&
  35350. chartX <= Math.min(scrollLeft + plotLeft + box.width, xAxis.pos + xAxis.len))) {
  35351. e.isInsidePlot = false;
  35352. }
  35353. }
  35354. if (!options.ignoreY && e.isInsidePlot) {
  35355. const yAxis = (!inverted && options.axis &&
  35356. !options.axis.isXAxis && options.axis) || (series && (inverted ? series.xAxis : series.yAxis)) || {
  35357. pos: plotTop,
  35358. len: Infinity
  35359. };
  35360. const chartY = options.paneCoordinates ?
  35361. yAxis.pos + y : plotTop + y;
  35362. if (!(chartY >= Math.max(scrollTop + plotTop, yAxis.pos) &&
  35363. chartY <= Math.min(scrollTop + plotTop + box.height, yAxis.pos + yAxis.len))) {
  35364. e.isInsidePlot = false;
  35365. }
  35366. }
  35367. fireEvent(this, 'afterIsInsidePlot', e);
  35368. return e.isInsidePlot;
  35369. }
  35370. /**
  35371. * Redraw the chart after changes have been done to the data, axis extremes
  35372. * chart size or chart elements. All methods for updating axes, series or
  35373. * points have a parameter for redrawing the chart. This is `true` by
  35374. * default. But in many cases you want to do more than one operation on the
  35375. * chart before redrawing, for example add a number of points. In those
  35376. * cases it is a waste of resources to redraw the chart for each new point
  35377. * added. So you add the points and call `chart.redraw()` after.
  35378. *
  35379. * @function Highcharts.Chart#redraw
  35380. *
  35381. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  35382. * If or how to apply animation to the redraw. When `undefined`, it applies
  35383. * the animation that is set in the `chart.animation` option.
  35384. *
  35385. * @emits Highcharts.Chart#event:afterSetExtremes
  35386. * @emits Highcharts.Chart#event:beforeRedraw
  35387. * @emits Highcharts.Chart#event:predraw
  35388. * @emits Highcharts.Chart#event:redraw
  35389. * @emits Highcharts.Chart#event:render
  35390. * @emits Highcharts.Chart#event:updatedData
  35391. */
  35392. redraw(animation) {
  35393. fireEvent(this, 'beforeRedraw');
  35394. const chart = this, axes = chart.hasCartesianSeries ? chart.axes : chart.colorAxis || [], series = chart.series, pointer = chart.pointer, legend = chart.legend, legendUserOptions = chart.userOptions.legend, renderer = chart.renderer, isHiddenChart = renderer.isHidden(), afterRedraw = [];
  35395. let hasDirtyStacks, hasStackedSeries, i, isDirtyBox = chart.isDirtyBox, redrawLegend = chart.isDirtyLegend, serie;
  35396. renderer.rootFontSize = renderer.boxWrapper.getStyle('font-size');
  35397. // Handle responsive rules, not only on resize (#6130)
  35398. if (chart.setResponsive) {
  35399. chart.setResponsive(false);
  35400. }
  35401. // Set the global animation. When chart.hasRendered is not true, the
  35402. // redraw call comes from a responsive rule and animation should not
  35403. // occur.
  35404. setAnimation(chart.hasRendered ? animation : false, chart);
  35405. if (isHiddenChart) {
  35406. chart.temporaryDisplay();
  35407. }
  35408. // Adjust title layout (reflow multiline text)
  35409. chart.layOutTitles(false);
  35410. // link stacked series
  35411. i = series.length;
  35412. while (i--) {
  35413. serie = series[i];
  35414. if (serie.options.stacking || serie.options.centerInCategory) {
  35415. hasStackedSeries = true;
  35416. if (serie.isDirty) {
  35417. hasDirtyStacks = true;
  35418. break;
  35419. }
  35420. }
  35421. }
  35422. if (hasDirtyStacks) { // mark others as dirty
  35423. i = series.length;
  35424. while (i--) {
  35425. serie = series[i];
  35426. if (serie.options.stacking) {
  35427. serie.isDirty = true;
  35428. }
  35429. }
  35430. }
  35431. // Handle updated data in the series
  35432. series.forEach(function (serie) {
  35433. if (serie.isDirty) {
  35434. if (serie.options.legendType === 'point') {
  35435. if (typeof serie.updateTotals === 'function') {
  35436. serie.updateTotals();
  35437. }
  35438. redrawLegend = true;
  35439. }
  35440. else if (legendUserOptions &&
  35441. (legendUserOptions.labelFormatter ||
  35442. legendUserOptions.labelFormat)) {
  35443. redrawLegend = true; // #2165
  35444. }
  35445. }
  35446. if (serie.isDirtyData) {
  35447. fireEvent(serie, 'updatedData');
  35448. }
  35449. });
  35450. // handle added or removed series
  35451. if (redrawLegend && legend && legend.options.enabled) {
  35452. // draw legend graphics
  35453. legend.render();
  35454. chart.isDirtyLegend = false;
  35455. }
  35456. // reset stacks
  35457. if (hasStackedSeries) {
  35458. chart.getStacks();
  35459. }
  35460. // set axes scales
  35461. axes.forEach(function (axis) {
  35462. axis.updateNames();
  35463. axis.setScale();
  35464. });
  35465. chart.getMargins(); // #3098
  35466. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  35467. axes.forEach(function (axis) {
  35468. if (axis.isDirty) {
  35469. isDirtyBox = true;
  35470. }
  35471. });
  35472. // redraw axes
  35473. axes.forEach(function (axis) {
  35474. // Fire 'afterSetExtremes' only if extremes are set
  35475. const key = axis.min + ',' + axis.max;
  35476. if (axis.extKey !== key) { // #821, #4452
  35477. axis.extKey = key;
  35478. // prevent a recursive call to chart.redraw() (#1119)
  35479. afterRedraw.push(function () {
  35480. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  35481. delete axis.eventArgs;
  35482. });
  35483. }
  35484. if (isDirtyBox || hasStackedSeries) {
  35485. axis.redraw();
  35486. }
  35487. });
  35488. // the plot areas size has changed
  35489. if (isDirtyBox) {
  35490. chart.drawChartBox();
  35491. }
  35492. // Fire an event before redrawing series, used by the boost module to
  35493. // clear previous series renderings.
  35494. fireEvent(chart, 'predraw');
  35495. // redraw affected series
  35496. series.forEach(function (serie) {
  35497. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  35498. serie.redraw();
  35499. }
  35500. // Set it here, otherwise we will have unlimited 'updatedData' calls
  35501. // for a hidden series after setData(). Fixes #6012
  35502. serie.isDirtyData = false;
  35503. });
  35504. // move tooltip or reset
  35505. if (pointer) {
  35506. pointer.reset(true);
  35507. }
  35508. // redraw if canvas
  35509. renderer.draw();
  35510. // Fire the events
  35511. fireEvent(chart, 'redraw');
  35512. fireEvent(chart, 'render');
  35513. if (isHiddenChart) {
  35514. chart.temporaryDisplay(true);
  35515. }
  35516. // Fire callbacks that are put on hold until after the redraw
  35517. afterRedraw.forEach(function (callback) {
  35518. callback.call();
  35519. });
  35520. }
  35521. /**
  35522. * Get an axis, series or point object by `id` as given in the configuration
  35523. * options. Returns `undefined` if no item is found.
  35524. *
  35525. * @sample highcharts/plotoptions/series-id/
  35526. * Get series by id
  35527. *
  35528. * @function Highcharts.Chart#get
  35529. *
  35530. * @param {string} id
  35531. * The id as given in the configuration options.
  35532. *
  35533. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  35534. * The retrieved item.
  35535. */
  35536. get(id) {
  35537. const series = this.series;
  35538. /**
  35539. * @private
  35540. */
  35541. function itemById(item) {
  35542. return (item.id === id ||
  35543. (item.options && item.options.id === id));
  35544. }
  35545. let ret =
  35546. // Search axes
  35547. find(this.axes, itemById) ||
  35548. // Search series
  35549. find(this.series, itemById);
  35550. // Search points
  35551. for (let i = 0; !ret && i < series.length; i++) {
  35552. ret = find(series[i].points || [], itemById);
  35553. }
  35554. return ret;
  35555. }
  35556. /**
  35557. * Create the Axis instances based on the config options.
  35558. *
  35559. * @private
  35560. * @function Highcharts.Chart#getAxes
  35561. * @emits Highcharts.Chart#event:afterGetAxes
  35562. * @emits Highcharts.Chart#event:getAxes
  35563. */
  35564. getAxes() {
  35565. const options = this.options;
  35566. fireEvent(this, 'getAxes');
  35567. for (const coll of ['xAxis', 'yAxis']) {
  35568. const arr = options[coll] = splat(options[coll] || {});
  35569. for (const axisOptions of arr) {
  35570. // eslint-disable-next-line no-new
  35571. new Axis(this, axisOptions, coll);
  35572. }
  35573. }
  35574. fireEvent(this, 'afterGetAxes');
  35575. }
  35576. /**
  35577. * Returns an array of all currently selected points in the chart. Points
  35578. * can be selected by clicking or programmatically by the
  35579. * {@link Highcharts.Point#select}
  35580. * function.
  35581. *
  35582. * @sample highcharts/plotoptions/series-allowpointselect-line/
  35583. * Get selected points
  35584. *
  35585. * @function Highcharts.Chart#getSelectedPoints
  35586. *
  35587. * @return {Array<Highcharts.Point>}
  35588. * The currently selected points.
  35589. */
  35590. getSelectedPoints() {
  35591. return this.series.reduce((acc, series) => {
  35592. // For one-to-one points inspect series.data in order to retrieve
  35593. // points outside the visible range (#6445). For grouped data,
  35594. // inspect the generated series.points.
  35595. series.getPointsCollection()
  35596. .forEach((point) => {
  35597. if (pick(point.selectedStaging, point.selected)) {
  35598. acc.push(point);
  35599. }
  35600. });
  35601. return acc;
  35602. }, []);
  35603. }
  35604. /**
  35605. * Returns an array of all currently selected series in the chart. Series
  35606. * can be selected either programmatically by the
  35607. * {@link Highcharts.Series#select}
  35608. * function or by checking the checkbox next to the legend item if
  35609. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  35610. * is true.
  35611. *
  35612. * @sample highcharts/members/chart-getselectedseries/
  35613. * Get selected series
  35614. *
  35615. * @function Highcharts.Chart#getSelectedSeries
  35616. *
  35617. * @return {Array<Highcharts.Series>}
  35618. * The currently selected series.
  35619. */
  35620. getSelectedSeries() {
  35621. return this.series.filter(function (serie) {
  35622. return serie.selected;
  35623. });
  35624. }
  35625. /**
  35626. * Set a new title or subtitle for the chart.
  35627. *
  35628. * @sample highcharts/members/chart-settitle/
  35629. * Set title text and styles
  35630. *
  35631. * @function Highcharts.Chart#setTitle
  35632. *
  35633. * @param {Highcharts.TitleOptions} [titleOptions]
  35634. * New title options. The title text itself is set by the
  35635. * `titleOptions.text` property.
  35636. *
  35637. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  35638. * New subtitle options. The subtitle text itself is set by the
  35639. * `subtitleOptions.text` property.
  35640. *
  35641. * @param {boolean} [redraw]
  35642. * Whether to redraw the chart or wait for a later call to
  35643. * `chart.redraw()`.
  35644. */
  35645. setTitle(titleOptions, subtitleOptions, redraw) {
  35646. this.applyDescription('title', titleOptions);
  35647. this.applyDescription('subtitle', subtitleOptions);
  35648. // The initial call also adds the caption. On update, chart.update will
  35649. // relay to Chart.setCaption.
  35650. this.applyDescription('caption', void 0);
  35651. this.layOutTitles(redraw);
  35652. }
  35653. /**
  35654. * Apply a title, subtitle or caption for the chart
  35655. *
  35656. * @private
  35657. * @function Highcharts.Chart#applyDescription
  35658. * @param name {string}
  35659. * Either title, subtitle or caption
  35660. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  35661. * The options to set, will be merged with default options.
  35662. */
  35663. applyDescription(name, explicitOptions) {
  35664. const chart = this;
  35665. // Merge default options with explicit options
  35666. const options = this.options[name] = merge(this.options[name], explicitOptions);
  35667. let elem = this[name];
  35668. if (elem && explicitOptions) {
  35669. this[name] = elem = elem.destroy(); // remove old
  35670. }
  35671. if (options && !elem) {
  35672. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  35673. .attr({
  35674. align: options.align,
  35675. 'class': 'highcharts-' + name,
  35676. zIndex: options.zIndex || 4
  35677. })
  35678. .add();
  35679. // Update methods, relay to `applyDescription`
  35680. elem.update = function (updateOptions, redraw) {
  35681. chart.applyDescription(name, updateOptions);
  35682. chart.layOutTitles(redraw);
  35683. };
  35684. // Presentational
  35685. if (!this.styledMode) {
  35686. elem.css(extend(name === 'title' ? {
  35687. // #2944
  35688. fontSize: this.options.isStock ? '1em' : '1.2em'
  35689. } : {}, options.style));
  35690. }
  35691. /**
  35692. * The chart title. The title has an `update` method that allows
  35693. * modifying the options directly or indirectly via
  35694. * `chart.update`.
  35695. *
  35696. * @sample highcharts/members/title-update/
  35697. * Updating titles
  35698. *
  35699. * @name Highcharts.Chart#title
  35700. * @type {Highcharts.TitleObject}
  35701. */
  35702. /**
  35703. * The chart subtitle. The subtitle has an `update` method that
  35704. * allows modifying the options directly or indirectly via
  35705. * `chart.update`.
  35706. *
  35707. * @name Highcharts.Chart#subtitle
  35708. * @type {Highcharts.SubtitleObject}
  35709. */
  35710. this[name] = elem;
  35711. }
  35712. }
  35713. /**
  35714. * Internal function to lay out the chart title, subtitle and caption, and
  35715. * cache the full offset height for use in `getMargins`. The result is
  35716. * stored in `this.titleOffset`.
  35717. *
  35718. * @private
  35719. * @function Highcharts.Chart#layOutTitles
  35720. *
  35721. * @param {boolean} [redraw=true]
  35722. * @emits Highcharts.Chart#event:afterLayOutTitles
  35723. */
  35724. layOutTitles(redraw = true) {
  35725. const titleOffset = [0, 0, 0], renderer = this.renderer, spacingBox = this.spacingBox;
  35726. // Lay out the title and the subtitle respectively
  35727. ['title', 'subtitle', 'caption'].forEach(function (key) {
  35728. const title = this[key], titleOptions = (this.options[key]), verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ?
  35729. verticalAlign === 'top' ? -3 : 0 :
  35730. // Floating subtitle (#6574)
  35731. verticalAlign === 'top' ? titleOffset[0] + 2 : 0;
  35732. if (title) {
  35733. title
  35734. .css({
  35735. width: (titleOptions.width ||
  35736. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  35737. });
  35738. const baseline = renderer.fontMetrics(title).b,
  35739. // Skip the cache for HTML (#3481, #11666)
  35740. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  35741. title.align(extend({
  35742. y: verticalAlign === 'bottom' ?
  35743. baseline :
  35744. offset + baseline,
  35745. height
  35746. }, titleOptions), false, 'spacingBox');
  35747. if (!titleOptions.floating) {
  35748. if (verticalAlign === 'top') {
  35749. titleOffset[0] = Math.ceil(titleOffset[0] +
  35750. height);
  35751. }
  35752. else if (verticalAlign === 'bottom') {
  35753. titleOffset[2] = Math.ceil(titleOffset[2] +
  35754. height);
  35755. }
  35756. }
  35757. }
  35758. }, this);
  35759. // Handle title.margin and caption.margin
  35760. if (titleOffset[0] &&
  35761. (this.options.title.verticalAlign || 'top') === 'top') {
  35762. titleOffset[0] += this.options.title.margin;
  35763. }
  35764. if (titleOffset[2] &&
  35765. this.options.caption.verticalAlign === 'bottom') {
  35766. titleOffset[2] += this.options.caption.margin;
  35767. }
  35768. const requiresDirtyBox = (!this.titleOffset ||
  35769. this.titleOffset.join(',') !== titleOffset.join(','));
  35770. // Used in getMargins
  35771. this.titleOffset = titleOffset;
  35772. fireEvent(this, 'afterLayOutTitles');
  35773. if (!this.isDirtyBox && requiresDirtyBox) {
  35774. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  35775. // Redraw if necessary (#2719, #2744)
  35776. if (this.hasRendered && redraw && this.isDirtyBox) {
  35777. this.redraw();
  35778. }
  35779. }
  35780. }
  35781. /**
  35782. * Internal function to get the available size of the container element
  35783. *
  35784. * @private
  35785. * @function Highcharts.Chart#getContainerBox
  35786. */
  35787. getContainerBox() {
  35788. return {
  35789. width: getStyle(this.renderTo, 'width', true) || 0,
  35790. height: getStyle(this.renderTo, 'height', true) || 0
  35791. };
  35792. }
  35793. /**
  35794. * Internal function to get the chart width and height according to options
  35795. * and container size. Sets {@link Chart.chartWidth} and
  35796. * {@link Chart.chartHeight}.
  35797. *
  35798. * @private
  35799. * @function Highcharts.Chart#getChartSize
  35800. */
  35801. getChartSize() {
  35802. const chart = this, optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, containerBox = chart.getContainerBox();
  35803. /**
  35804. * The current pixel width of the chart.
  35805. *
  35806. * @name Highcharts.Chart#chartWidth
  35807. * @type {number}
  35808. */
  35809. chart.chartWidth = Math.max(// #1393
  35810. 0, widthOption || containerBox.width || 600 // #1460
  35811. );
  35812. /**
  35813. * The current pixel height of the chart.
  35814. *
  35815. * @name Highcharts.Chart#chartHeight
  35816. * @type {number}
  35817. */
  35818. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  35819. (containerBox.height > 1 ? containerBox.height : 400));
  35820. chart.containerBox = containerBox;
  35821. }
  35822. /**
  35823. * If the renderTo element has no offsetWidth, most likely one or more of
  35824. * its parents are hidden. Loop up the DOM tree to temporarily display the
  35825. * parents, then save the original display properties, and when the true
  35826. * size is retrieved, reset them. Used on first render and on redraws.
  35827. *
  35828. * @private
  35829. * @function Highcharts.Chart#temporaryDisplay
  35830. *
  35831. * @param {boolean} [revert]
  35832. * Revert to the saved original styles.
  35833. */
  35834. temporaryDisplay(revert) {
  35835. let node = this.renderTo, tempStyle;
  35836. if (!revert) {
  35837. while (node && node.style) {
  35838. // When rendering to a detached node, it needs to be temporarily
  35839. // attached in order to read styling and bounding boxes (#5783,
  35840. // #7024).
  35841. if (!doc.body.contains(node) && !node.parentNode) {
  35842. node.hcOrigDetached = true;
  35843. doc.body.appendChild(node);
  35844. }
  35845. if (getStyle(node, 'display', false) === 'none' ||
  35846. node.hcOricDetached) {
  35847. node.hcOrigStyle = {
  35848. display: node.style.display,
  35849. height: node.style.height,
  35850. overflow: node.style.overflow
  35851. };
  35852. tempStyle = {
  35853. display: 'block',
  35854. overflow: 'hidden'
  35855. };
  35856. if (node !== this.renderTo) {
  35857. tempStyle.height = 0;
  35858. }
  35859. css(node, tempStyle);
  35860. // If it still doesn't have an offset width after setting
  35861. // display to block, it probably has an !important priority
  35862. // #2631, 6803
  35863. if (!node.offsetWidth) {
  35864. node.style.setProperty('display', 'block', 'important');
  35865. }
  35866. }
  35867. node = node.parentNode;
  35868. if (node === doc.body) {
  35869. break;
  35870. }
  35871. }
  35872. }
  35873. else {
  35874. while (node && node.style) {
  35875. if (node.hcOrigStyle) {
  35876. css(node, node.hcOrigStyle);
  35877. delete node.hcOrigStyle;
  35878. }
  35879. if (node.hcOrigDetached) {
  35880. doc.body.removeChild(node);
  35881. node.hcOrigDetached = false;
  35882. }
  35883. node = node.parentNode;
  35884. }
  35885. }
  35886. }
  35887. /**
  35888. * Set the {@link Chart.container|chart container's} class name, in
  35889. * addition to `highcharts-container`.
  35890. *
  35891. * @function Highcharts.Chart#setClassName
  35892. *
  35893. * @param {string} [className]
  35894. * The additional class name.
  35895. */
  35896. setClassName(className) {
  35897. this.container.className = 'highcharts-container ' + (className || '');
  35898. }
  35899. /**
  35900. * Get the containing element, determine the size and create the inner
  35901. * container div to hold the chart.
  35902. *
  35903. * @private
  35904. * @function Highcharts.Chart#afterGetContainer
  35905. * @emits Highcharts.Chart#event:afterGetContainer
  35906. */
  35907. getContainer() {
  35908. const chart = this, options = chart.options, optionsChart = options.chart, indexAttrName = 'data-highcharts-chart', containerId = uniqueKey();
  35909. let containerStyle, renderTo = chart.renderTo;
  35910. if (!renderTo) {
  35911. chart.renderTo = renderTo =
  35912. optionsChart.renderTo;
  35913. }
  35914. if (isString(renderTo)) {
  35915. chart.renderTo = renderTo =
  35916. doc.getElementById(renderTo);
  35917. }
  35918. // Display an error if the renderTo is wrong
  35919. if (!renderTo) {
  35920. error(13, true, chart);
  35921. }
  35922. // If the container already holds a chart, destroy it. The check for
  35923. // hasRendered is there because web pages that are saved to disk from
  35924. // the browser, will preserve the data-highcharts-chart attribute and
  35925. // the SVG contents, but not an interactive chart. So in this case,
  35926. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  35927. const oldChartIndex = pInt(attr(renderTo, indexAttrName));
  35928. if (isNumber(oldChartIndex) &&
  35929. charts[oldChartIndex] &&
  35930. charts[oldChartIndex].hasRendered) {
  35931. charts[oldChartIndex].destroy();
  35932. }
  35933. // Make a reference to the chart from the div
  35934. attr(renderTo, indexAttrName, chart.index);
  35935. // remove previous chart
  35936. renderTo.innerHTML = AST.emptyHTML;
  35937. // If the container doesn't have an offsetWidth, it has or is a child of
  35938. // a node that has display:none. We need to temporarily move it out to a
  35939. // visible state to determine the size, else the legend and tooltips
  35940. // won't render properly. The skipClone option is used in sparklines as
  35941. // a micro optimization, saving about 1-2 ms each chart.
  35942. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  35943. chart.temporaryDisplay();
  35944. }
  35945. // get the width and height
  35946. chart.getChartSize();
  35947. const chartWidth = chart.chartWidth;
  35948. const chartHeight = chart.chartHeight;
  35949. // Allow table cells and flex-boxes to shrink without the chart blocking
  35950. // them out (#6427)
  35951. css(renderTo, { overflow: 'hidden' });
  35952. // Create the inner container
  35953. if (!chart.styledMode) {
  35954. containerStyle = extend({
  35955. position: 'relative',
  35956. // needed for context menu (avoidscrollbars) and content
  35957. // overflow in IE
  35958. overflow: 'hidden',
  35959. width: chartWidth + 'px',
  35960. height: chartHeight + 'px',
  35961. textAlign: 'left',
  35962. lineHeight: 'normal',
  35963. zIndex: 0,
  35964. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
  35965. userSelect: 'none',
  35966. 'touch-action': 'manipulation',
  35967. outline: 'none'
  35968. }, optionsChart.style || {});
  35969. }
  35970. /**
  35971. * The containing HTML element of the chart. The container is
  35972. * dynamically inserted into the element given as the `renderTo`
  35973. * parameter in the {@link Highcharts#chart} constructor.
  35974. *
  35975. * @name Highcharts.Chart#container
  35976. * @type {Highcharts.HTMLDOMElement}
  35977. */
  35978. const container = createElement('div', {
  35979. id: containerId
  35980. }, containerStyle, renderTo);
  35981. chart.container = container;
  35982. // cache the cursor (#1650)
  35983. chart._cursor = container.style.cursor;
  35984. // Initialize the renderer
  35985. const Renderer = optionsChart.renderer || !svg ?
  35986. RendererRegistry.getRendererType(optionsChart.renderer) :
  35987. SVGRenderer;
  35988. /**
  35989. * The renderer instance of the chart. Each chart instance has only one
  35990. * associated renderer.
  35991. *
  35992. * @name Highcharts.Chart#renderer
  35993. * @type {Highcharts.SVGRenderer}
  35994. */
  35995. chart.renderer = new Renderer(container, chartWidth, chartHeight, void 0, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  35996. chart.containerBox = chart.getContainerBox();
  35997. // Set the initial animation from the options
  35998. setAnimation(void 0, chart);
  35999. chart.setClassName(optionsChart.className);
  36000. if (!chart.styledMode) {
  36001. chart.renderer.setStyle(optionsChart.style);
  36002. }
  36003. else {
  36004. // Initialize definitions
  36005. for (const key in options.defs) { // eslint-disable-line guard-for-in
  36006. this.renderer.definition(options.defs[key]);
  36007. }
  36008. }
  36009. // Add a reference to the charts index
  36010. chart.renderer.chartIndex = chart.index;
  36011. fireEvent(this, 'afterGetContainer');
  36012. }
  36013. /**
  36014. * Calculate margins by rendering axis labels in a preliminary position.
  36015. * Title, subtitle and legend have already been rendered at this stage, but
  36016. * will be moved into their final positions.
  36017. *
  36018. * @private
  36019. * @function Highcharts.Chart#getMargins
  36020. * @emits Highcharts.Chart#event:getMargins
  36021. */
  36022. getMargins(skipAxes) {
  36023. const { spacing, margin, titleOffset } = this;
  36024. this.resetMargins();
  36025. // Adjust for title and subtitle
  36026. if (titleOffset[0] && !defined(margin[0])) {
  36027. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  36028. }
  36029. if (titleOffset[2] && !defined(margin[2])) {
  36030. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  36031. }
  36032. // Adjust for legend
  36033. if (this.legend && this.legend.display) {
  36034. this.legend.adjustMargins(margin, spacing);
  36035. }
  36036. fireEvent(this, 'getMargins');
  36037. if (!skipAxes) {
  36038. this.getAxisMargins();
  36039. }
  36040. }
  36041. /**
  36042. * @private
  36043. * @function Highcharts.Chart#getAxisMargins
  36044. */
  36045. getAxisMargins() {
  36046. const chart = this,
  36047. // [top, right, bottom, left]
  36048. axisOffset = chart.axisOffset = [0, 0, 0, 0], colorAxis = chart.colorAxis, margin = chart.margin, getOffset = function (axes) {
  36049. axes.forEach(function (axis) {
  36050. if (axis.visible) {
  36051. axis.getOffset();
  36052. }
  36053. });
  36054. };
  36055. // pre-render axes to get labels offset width
  36056. if (chart.hasCartesianSeries) {
  36057. getOffset(chart.axes);
  36058. }
  36059. else if (colorAxis && colorAxis.length) {
  36060. getOffset(colorAxis);
  36061. }
  36062. // Add the axis offsets
  36063. marginNames.forEach(function (m, side) {
  36064. if (!defined(margin[side])) {
  36065. chart[m] += axisOffset[side];
  36066. }
  36067. });
  36068. chart.setChartSize();
  36069. }
  36070. /**
  36071. * Return the current options of the chart, but only those that differ from
  36072. * default options. Items that can be either an object or an array of
  36073. * objects, like `series`, `xAxis` and `yAxis`, are always returned as
  36074. * array.
  36075. *
  36076. * @sample highcharts/members/chart-getoptions
  36077. *
  36078. * @function Highcharts.Chart#getOptions
  36079. *
  36080. * @since 11.1.0
  36081. */
  36082. getOptions() {
  36083. return diffObjects(this.userOptions, defaultOptions);
  36084. }
  36085. /**
  36086. * Reflows the chart to its container. By default, the Resize Observer is
  36087. * attached to the chart's div which allows to reflows the chart
  36088. * automatically to its container, as per the
  36089. * [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  36090. * option.
  36091. *
  36092. * @sample highcharts/chart/events-container/
  36093. * Pop up and reflow
  36094. *
  36095. * @function Highcharts.Chart#reflow
  36096. *
  36097. * @param {global.Event} [e]
  36098. * Event arguments. Used primarily when the function is called
  36099. * internally as a response to window resize.
  36100. */
  36101. reflow(e) {
  36102. const chart = this, optionsChart = chart.options.chart, hasUserSize = (defined(optionsChart.width) &&
  36103. defined(optionsChart.height)), oldBox = chart.containerBox, containerBox = chart.getContainerBox();
  36104. delete chart.pointer.chartPosition;
  36105. // Width and height checks for display:none. Target is doc in Opera
  36106. // and win in Firefox, Chrome and IE9.
  36107. if (!hasUserSize &&
  36108. !chart.isPrinting &&
  36109. oldBox &&
  36110. // When fired by resize observer inside hidden container
  36111. containerBox.width) {
  36112. if (containerBox.width !== oldBox.width ||
  36113. containerBox.height !== oldBox.height) {
  36114. U.clearTimeout(chart.reflowTimeout);
  36115. // When called from window.resize, e is set, else it's called
  36116. // directly (#2224)
  36117. chart.reflowTimeout = syncTimeout(function () {
  36118. // Set size, it may have been destroyed in the meantime
  36119. // (#1257)
  36120. if (chart.container) {
  36121. chart.setSize(void 0, void 0, false);
  36122. }
  36123. }, e ? 100 : 0);
  36124. }
  36125. chart.containerBox = containerBox;
  36126. }
  36127. }
  36128. /**
  36129. * Toggle the event handlers necessary for auto resizing, depending on the
  36130. * `chart.reflow` option.
  36131. *
  36132. * @private
  36133. * @function Highcharts.Chart#setReflow
  36134. */
  36135. setReflow() {
  36136. const chart = this;
  36137. const runReflow = (e) => {
  36138. var _a;
  36139. if (((_a = chart.options) === null || _a === void 0 ? void 0 : _a.chart.reflow) && chart.hasLoaded) {
  36140. chart.reflow(e);
  36141. }
  36142. };
  36143. if (typeof ResizeObserver === 'function') {
  36144. (new ResizeObserver(runReflow)).observe(chart.renderTo);
  36145. // Fallback for more legacy browser versions.
  36146. }
  36147. else {
  36148. const unbind = addEvent(win, 'resize', runReflow);
  36149. addEvent(this, 'destroy', unbind);
  36150. }
  36151. }
  36152. /**
  36153. * Resize the chart to a given width and height. In order to set the width
  36154. * only, the height argument may be skipped. To set the height only, pass
  36155. * `undefined` for the width.
  36156. *
  36157. * @sample highcharts/members/chart-setsize-button/
  36158. * Test resizing from buttons
  36159. * @sample highcharts/members/chart-setsize-jquery-resizable/
  36160. * Add a jQuery UI resizable
  36161. * @sample stock/members/chart-setsize/
  36162. * Highcharts Stock with UI resizable
  36163. *
  36164. * @function Highcharts.Chart#setSize
  36165. *
  36166. * @param {number|null} [width]
  36167. * The new pixel width of the chart. Since v4.2.6, the argument can
  36168. * be `undefined` in order to preserve the current value (when
  36169. * setting height only), or `null` to adapt to the width of the
  36170. * containing element.
  36171. *
  36172. * @param {number|null} [height]
  36173. * The new pixel height of the chart. Since v4.2.6, the argument can
  36174. * be `undefined` in order to preserve the current value, or `null`
  36175. * in order to adapt to the height of the containing element.
  36176. *
  36177. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36178. * Whether and how to apply animation. When `undefined`, it applies
  36179. * the animation that is set in the `chart.animation` option.
  36180. *
  36181. *
  36182. * @emits Highcharts.Chart#event:endResize
  36183. * @emits Highcharts.Chart#event:resize
  36184. */
  36185. setSize(width, height, animation) {
  36186. const chart = this, renderer = chart.renderer;
  36187. // Handle the isResizing counter
  36188. chart.isResizing += 1;
  36189. // set the animation for the current process
  36190. setAnimation(animation, chart);
  36191. const globalAnimation = renderer.globalAnimation;
  36192. chart.oldChartHeight = chart.chartHeight;
  36193. chart.oldChartWidth = chart.chartWidth;
  36194. if (typeof width !== 'undefined') {
  36195. chart.options.chart.width = width;
  36196. }
  36197. if (typeof height !== 'undefined') {
  36198. chart.options.chart.height = height;
  36199. }
  36200. chart.getChartSize();
  36201. // Resize the container with the global animation applied if enabled
  36202. // (#2503)
  36203. if (!chart.styledMode) {
  36204. (globalAnimation ? animate : css)(chart.container, {
  36205. width: chart.chartWidth + 'px',
  36206. height: chart.chartHeight + 'px'
  36207. }, globalAnimation);
  36208. }
  36209. chart.setChartSize(true);
  36210. renderer.setSize(chart.chartWidth, chart.chartHeight, globalAnimation);
  36211. // handle axes
  36212. chart.axes.forEach(function (axis) {
  36213. axis.isDirty = true;
  36214. axis.setScale();
  36215. });
  36216. chart.isDirtyLegend = true; // force legend redraw
  36217. chart.isDirtyBox = true; // force redraw of plot and chart border
  36218. chart.layOutTitles(); // #2857
  36219. chart.getMargins();
  36220. chart.redraw(globalAnimation);
  36221. chart.oldChartHeight = null;
  36222. fireEvent(chart, 'resize');
  36223. // Fire endResize and set isResizing back. If animation is disabled,
  36224. // fire without delay
  36225. syncTimeout(function () {
  36226. if (chart) {
  36227. fireEvent(chart, 'endResize', null, function () {
  36228. chart.isResizing -= 1;
  36229. });
  36230. }
  36231. }, animObject(globalAnimation).duration);
  36232. }
  36233. /**
  36234. * Set the public chart properties. This is done before and after the
  36235. * pre-render to determine margin sizes.
  36236. *
  36237. * @private
  36238. * @function Highcharts.Chart#setChartSize
  36239. * @emits Highcharts.Chart#event:afterSetChartSize
  36240. */
  36241. setChartSize(skipAxes) {
  36242. const chart = this, inverted = chart.inverted, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, optionsChart = chart.options.chart, spacing = chart.spacing, clipOffset = chart.clipOffset;
  36243. let plotLeft, plotTop, plotWidth, plotHeight;
  36244. /**
  36245. * The current left position of the plot area in pixels.
  36246. *
  36247. * @name Highcharts.Chart#plotLeft
  36248. * @type {number}
  36249. */
  36250. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  36251. /**
  36252. * The current top position of the plot area in pixels.
  36253. *
  36254. * @name Highcharts.Chart#plotTop
  36255. * @type {number}
  36256. */
  36257. chart.plotTop = plotTop = Math.round(chart.plotTop);
  36258. /**
  36259. * The current width of the plot area in pixels.
  36260. *
  36261. * @name Highcharts.Chart#plotWidth
  36262. * @type {number}
  36263. */
  36264. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  36265. /**
  36266. * The current height of the plot area in pixels.
  36267. *
  36268. * @name Highcharts.Chart#plotHeight
  36269. * @type {number}
  36270. */
  36271. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  36272. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  36273. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  36274. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  36275. // Set boxes used for alignment
  36276. chart.spacingBox = renderer.spacingBox = {
  36277. x: spacing[3],
  36278. y: spacing[0],
  36279. width: chartWidth - spacing[3] - spacing[1],
  36280. height: chartHeight - spacing[0] - spacing[2]
  36281. };
  36282. chart.plotBox = renderer.plotBox = {
  36283. x: plotLeft,
  36284. y: plotTop,
  36285. width: plotWidth,
  36286. height: plotHeight
  36287. };
  36288. const plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2), clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2), clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  36289. chart.clipBox = {
  36290. x: clipX,
  36291. y: clipY,
  36292. width: Math.floor(chart.plotSizeX -
  36293. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  36294. clipX),
  36295. height: Math.max(0, Math.floor(chart.plotSizeY -
  36296. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  36297. clipY))
  36298. };
  36299. if (!skipAxes) {
  36300. chart.axes.forEach(function (axis) {
  36301. axis.setAxisSize();
  36302. axis.setAxisTranslation();
  36303. });
  36304. renderer.alignElements();
  36305. }
  36306. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  36307. }
  36308. /**
  36309. * Initial margins before auto size margins are applied.
  36310. *
  36311. * @private
  36312. * @function Highcharts.Chart#resetMargins
  36313. */
  36314. resetMargins() {
  36315. fireEvent(this, 'resetMargins');
  36316. const chart = this, chartOptions = chart.options.chart;
  36317. // Create margin and spacing array
  36318. ['margin', 'spacing'].forEach(function splashArrays(target) {
  36319. const value = chartOptions[target], values = isObject(value) ? value : [value, value, value, value];
  36320. [
  36321. 'Top',
  36322. 'Right',
  36323. 'Bottom',
  36324. 'Left'
  36325. ].forEach(function (sideName, side) {
  36326. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  36327. });
  36328. });
  36329. // Set margin names like chart.plotTop, chart.plotLeft,
  36330. // chart.marginRight, chart.marginBottom.
  36331. marginNames.forEach(function (m, side) {
  36332. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  36333. });
  36334. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  36335. chart.clipOffset = [0, 0, 0, 0];
  36336. }
  36337. /**
  36338. * Internal function to draw or redraw the borders and backgrounds for chart
  36339. * and plot area.
  36340. *
  36341. * @private
  36342. * @function Highcharts.Chart#drawChartBox
  36343. * @emits Highcharts.Chart#event:afterDrawChartBox
  36344. */
  36345. drawChartBox() {
  36346. const chart = this, optionsChart = chart.options.chart, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, styledMode = chart.styledMode, plotBGImage = chart.plotBGImage, chartBackgroundColor = optionsChart.backgroundColor, plotBackgroundColor = optionsChart.plotBackgroundColor, plotBackgroundImage = optionsChart.plotBackgroundImage, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotBox = chart.plotBox, clipRect = chart.clipRect, clipBox = chart.clipBox;
  36347. let chartBackground = chart.chartBackground, plotBackground = chart.plotBackground, plotBorder = chart.plotBorder, chartBorderWidth, mgn, bgAttr, verb = 'animate';
  36348. // Chart area
  36349. if (!chartBackground) {
  36350. chart.chartBackground = chartBackground = renderer.rect()
  36351. .addClass('highcharts-background')
  36352. .add();
  36353. verb = 'attr';
  36354. }
  36355. if (!styledMode) {
  36356. // Presentational
  36357. chartBorderWidth = optionsChart.borderWidth || 0;
  36358. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  36359. bgAttr = {
  36360. fill: chartBackgroundColor || 'none'
  36361. };
  36362. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  36363. bgAttr.stroke = optionsChart.borderColor;
  36364. bgAttr['stroke-width'] = chartBorderWidth;
  36365. }
  36366. chartBackground
  36367. .attr(bgAttr)
  36368. .shadow(optionsChart.shadow);
  36369. }
  36370. else {
  36371. chartBorderWidth = mgn = chartBackground.strokeWidth();
  36372. }
  36373. chartBackground[verb]({
  36374. x: mgn / 2,
  36375. y: mgn / 2,
  36376. width: chartWidth - mgn - chartBorderWidth % 2,
  36377. height: chartHeight - mgn - chartBorderWidth % 2,
  36378. r: optionsChart.borderRadius
  36379. });
  36380. // Plot background
  36381. verb = 'animate';
  36382. if (!plotBackground) {
  36383. verb = 'attr';
  36384. chart.plotBackground = plotBackground = renderer.rect()
  36385. .addClass('highcharts-plot-background')
  36386. .add();
  36387. }
  36388. plotBackground[verb](plotBox);
  36389. if (!styledMode) {
  36390. // Presentational attributes for the background
  36391. plotBackground
  36392. .attr({
  36393. fill: plotBackgroundColor || 'none'
  36394. })
  36395. .shadow(optionsChart.plotShadow);
  36396. // Create the background image
  36397. if (plotBackgroundImage) {
  36398. if (!plotBGImage) {
  36399. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  36400. }
  36401. else {
  36402. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  36403. plotBGImage.attr('href', plotBackgroundImage);
  36404. }
  36405. plotBGImage.animate(plotBox);
  36406. }
  36407. }
  36408. }
  36409. // Plot clip
  36410. if (!clipRect) {
  36411. chart.clipRect = renderer.clipRect(clipBox);
  36412. }
  36413. else {
  36414. clipRect.animate({
  36415. width: clipBox.width,
  36416. height: clipBox.height
  36417. });
  36418. }
  36419. // Plot area border
  36420. verb = 'animate';
  36421. if (!plotBorder) {
  36422. verb = 'attr';
  36423. chart.plotBorder = plotBorder = renderer.rect()
  36424. .addClass('highcharts-plot-border')
  36425. .attr({
  36426. zIndex: 1 // Above the grid
  36427. })
  36428. .add();
  36429. }
  36430. if (!styledMode) {
  36431. // Presentational
  36432. plotBorder.attr({
  36433. stroke: optionsChart.plotBorderColor,
  36434. 'stroke-width': optionsChart.plotBorderWidth || 0,
  36435. fill: 'none'
  36436. });
  36437. }
  36438. plotBorder[verb](plotBorder.crisp({
  36439. x: plotLeft,
  36440. y: plotTop,
  36441. width: plotWidth,
  36442. height: plotHeight
  36443. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  36444. // reset
  36445. chart.isDirtyBox = false;
  36446. fireEvent(this, 'afterDrawChartBox');
  36447. }
  36448. /**
  36449. * Detect whether a certain chart property is needed based on inspecting its
  36450. * options and series. This mainly applies to the chart.inverted property,
  36451. * and in extensions to the chart.angular and chart.polar properties.
  36452. *
  36453. * @private
  36454. * @function Highcharts.Chart#propFromSeries
  36455. */
  36456. propFromSeries() {
  36457. const chart = this, optionsChart = chart.options.chart, seriesOptions = chart.options.series;
  36458. let i, klass, value;
  36459. /**
  36460. * The flag is set to `true` if a series of the chart is inverted.
  36461. *
  36462. * @name Highcharts.Chart#inverted
  36463. * @type {boolean|undefined}
  36464. */
  36465. ['inverted', 'angular', 'polar'].forEach(function (key) {
  36466. // The default series type's class
  36467. klass = seriesTypes[optionsChart.type];
  36468. // Get the value from available chart-wide properties
  36469. value =
  36470. // It is set in the options:
  36471. optionsChart[key] ||
  36472. // The default series class:
  36473. (klass && klass.prototype[key]);
  36474. // requires it
  36475. // 4. Check if any the chart's series require it
  36476. i = seriesOptions && seriesOptions.length;
  36477. while (!value && i--) {
  36478. klass = seriesTypes[seriesOptions[i].type];
  36479. if (klass && klass.prototype[key]) {
  36480. value = true;
  36481. }
  36482. }
  36483. // Set the chart property
  36484. chart[key] = value;
  36485. });
  36486. }
  36487. /**
  36488. * Internal function to link two or more series together, based on the
  36489. * `linkedTo` option. This is done from `Chart.render`, and after
  36490. * `Chart.addSeries` and `Series.remove`.
  36491. *
  36492. * @private
  36493. * @function Highcharts.Chart#linkSeries
  36494. * @emits Highcharts.Chart#event:afterLinkSeries
  36495. */
  36496. linkSeries(isUpdating) {
  36497. const chart = this, chartSeries = chart.series;
  36498. // Reset links
  36499. chartSeries.forEach(function (series) {
  36500. series.linkedSeries.length = 0;
  36501. });
  36502. // Apply new links
  36503. chartSeries.forEach(function (series) {
  36504. let linkedTo = series.options.linkedTo;
  36505. if (isString(linkedTo)) {
  36506. if (linkedTo === ':previous') {
  36507. linkedTo = chart.series[series.index - 1];
  36508. }
  36509. else {
  36510. linkedTo = chart.get(linkedTo);
  36511. }
  36512. // #3341 avoid mutual linking
  36513. if (linkedTo && linkedTo.linkedParent !== series) {
  36514. linkedTo.linkedSeries.push(series);
  36515. series.linkedParent = linkedTo;
  36516. if (linkedTo.enabledDataSorting) {
  36517. series.setDataSortingOptions();
  36518. }
  36519. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  36520. }
  36521. }
  36522. });
  36523. fireEvent(this, 'afterLinkSeries', { isUpdating });
  36524. }
  36525. /**
  36526. * Render series for the chart.
  36527. *
  36528. * @private
  36529. * @function Highcharts.Chart#renderSeries
  36530. */
  36531. renderSeries() {
  36532. this.series.forEach(function (serie) {
  36533. serie.translate();
  36534. serie.render();
  36535. });
  36536. }
  36537. /**
  36538. * Render all graphics for the chart. Runs internally on initialization.
  36539. *
  36540. * @private
  36541. * @function Highcharts.Chart#render
  36542. */
  36543. render() {
  36544. const chart = this, axes = chart.axes, colorAxis = chart.colorAxis, renderer = chart.renderer, renderAxes = function (axes) {
  36545. axes.forEach(function (axis) {
  36546. if (axis.visible) {
  36547. axis.render();
  36548. }
  36549. });
  36550. };
  36551. let correction = 0; // correction for X axis labels
  36552. // Title
  36553. chart.setTitle();
  36554. // Fire an event before the margins are computed. This is where the
  36555. // legend is assigned.
  36556. fireEvent(chart, 'beforeMargins');
  36557. // Get stacks
  36558. if (chart.getStacks) {
  36559. chart.getStacks();
  36560. }
  36561. // Get chart margins
  36562. chart.getMargins(true);
  36563. chart.setChartSize();
  36564. // Record preliminary dimensions for later comparison
  36565. const tempWidth = chart.plotWidth;
  36566. axes.some(function (axis) {
  36567. if (axis.horiz &&
  36568. axis.visible &&
  36569. axis.options.labels.enabled &&
  36570. axis.series.length) {
  36571. // 21 is the most common correction for X axis labels
  36572. correction = 21;
  36573. return true;
  36574. }
  36575. });
  36576. // use Math.max to prevent negative plotHeight
  36577. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  36578. const tempHeight = chart.plotHeight;
  36579. // Get margins by pre-rendering axes
  36580. axes.forEach(function (axis) {
  36581. axis.setScale();
  36582. });
  36583. chart.getAxisMargins();
  36584. // If the plot area size has changed significantly, calculate tick
  36585. // positions again
  36586. const redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  36587. // Height is more sensitive, use lower threshold
  36588. const redoVertical = tempHeight / chart.plotHeight > 1.05;
  36589. if (redoHorizontal || redoVertical) {
  36590. axes.forEach(function (axis) {
  36591. if ((axis.horiz && redoHorizontal) ||
  36592. (!axis.horiz && redoVertical)) {
  36593. // update to reflect the new margins
  36594. axis.setTickInterval(true);
  36595. }
  36596. });
  36597. chart.getMargins(); // second pass to check for new labels
  36598. }
  36599. // Draw the borders and backgrounds
  36600. chart.drawChartBox();
  36601. // Axes
  36602. if (chart.hasCartesianSeries) {
  36603. renderAxes(axes);
  36604. }
  36605. else if (colorAxis && colorAxis.length) {
  36606. renderAxes(colorAxis);
  36607. }
  36608. // The series
  36609. if (!chart.seriesGroup) {
  36610. chart.seriesGroup = renderer.g('series-group')
  36611. .attr({ zIndex: 3 })
  36612. .shadow(chart.options.chart.seriesGroupShadow)
  36613. .add();
  36614. }
  36615. chart.renderSeries();
  36616. // Credits
  36617. chart.addCredits();
  36618. // Handle responsiveness
  36619. if (chart.setResponsive) {
  36620. chart.setResponsive();
  36621. }
  36622. // Set flag
  36623. chart.hasRendered = true;
  36624. }
  36625. /**
  36626. * Set a new credits label for the chart.
  36627. *
  36628. * @sample highcharts/credits/credits-update/
  36629. * Add and update credits
  36630. *
  36631. * @function Highcharts.Chart#addCredits
  36632. *
  36633. * @param {Highcharts.CreditsOptions} [credits]
  36634. * A configuration object for the new credits.
  36635. */
  36636. addCredits(credits) {
  36637. const chart = this, creds = merge(true, this.options.credits, credits);
  36638. if (creds.enabled && !this.credits) {
  36639. /**
  36640. * The chart's credits label. The label has an `update` method that
  36641. * allows setting new options as per the
  36642. * [credits options set](https://api.highcharts.com/highcharts/credits).
  36643. *
  36644. * @name Highcharts.Chart#credits
  36645. * @type {Highcharts.SVGElement}
  36646. */
  36647. this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
  36648. .addClass('highcharts-credits')
  36649. .on('click', function () {
  36650. if (creds.href) {
  36651. win.location.href = creds.href;
  36652. }
  36653. })
  36654. .attr({
  36655. align: creds.position.align,
  36656. zIndex: 8
  36657. });
  36658. if (!chart.styledMode) {
  36659. this.credits.css(creds.style);
  36660. }
  36661. this.credits
  36662. .add()
  36663. .align(creds.position);
  36664. // Dynamically update
  36665. this.credits.update = function (options) {
  36666. chart.credits = chart.credits.destroy();
  36667. chart.addCredits(options);
  36668. };
  36669. }
  36670. }
  36671. /**
  36672. * Remove the chart and purge memory. This method is called internally
  36673. * before adding a second chart into the same container, as well as on
  36674. * window unload to prevent leaks.
  36675. *
  36676. * @sample highcharts/members/chart-destroy/
  36677. * Destroy the chart from a button
  36678. * @sample stock/members/chart-destroy/
  36679. * Destroy with Highcharts Stock
  36680. *
  36681. * @function Highcharts.Chart#destroy
  36682. *
  36683. * @emits Highcharts.Chart#event:destroy
  36684. */
  36685. destroy() {
  36686. const chart = this, axes = chart.axes, series = chart.series, container = chart.container, parentNode = container && container.parentNode;
  36687. let i;
  36688. // fire the chart.destoy event
  36689. fireEvent(chart, 'destroy');
  36690. // Delete the chart from charts lookup array
  36691. if (chart.renderer.forExport) {
  36692. erase(charts, chart); // #6569
  36693. }
  36694. else {
  36695. charts[chart.index] = void 0;
  36696. }
  36697. H.chartCount--;
  36698. chart.renderTo.removeAttribute('data-highcharts-chart');
  36699. // remove events
  36700. removeEvent(chart);
  36701. // ==== Destroy collections:
  36702. // Destroy axes
  36703. i = axes.length;
  36704. while (i--) {
  36705. axes[i] = axes[i].destroy();
  36706. }
  36707. // Destroy scroller & scroller series before destroying base series
  36708. if (this.scroller && this.scroller.destroy) {
  36709. this.scroller.destroy();
  36710. }
  36711. // Destroy each series
  36712. i = series.length;
  36713. while (i--) {
  36714. series[i] = series[i].destroy();
  36715. }
  36716. // ==== Destroy chart properties:
  36717. [
  36718. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  36719. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  36720. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  36721. 'renderer'
  36722. ].forEach(function (name) {
  36723. const prop = chart[name];
  36724. if (prop && prop.destroy) {
  36725. chart[name] = prop.destroy();
  36726. }
  36727. });
  36728. // Remove container and all SVG, check container as it can break in IE
  36729. // when destroyed before finished loading
  36730. if (container) {
  36731. container.innerHTML = AST.emptyHTML;
  36732. removeEvent(container);
  36733. if (parentNode) {
  36734. discardElement(container);
  36735. }
  36736. }
  36737. // clean it all up
  36738. objectEach(chart, function (val, key) {
  36739. delete chart[key];
  36740. });
  36741. }
  36742. /**
  36743. * Prepare for first rendering after all data are loaded.
  36744. *
  36745. * @private
  36746. * @function Highcharts.Chart#firstRender
  36747. * @emits Highcharts.Chart#event:beforeRender
  36748. */
  36749. firstRender() {
  36750. const chart = this, options = chart.options;
  36751. // Create the container
  36752. chart.getContainer();
  36753. chart.resetMargins();
  36754. chart.setChartSize();
  36755. // Set the common chart properties (mainly invert) from the given series
  36756. chart.propFromSeries();
  36757. // get axes
  36758. chart.getAxes();
  36759. // Initialize the series
  36760. const series = isArray(options.series) ? options.series : [];
  36761. options.series = []; // Avoid mutation
  36762. series.forEach(
  36763. // #9680
  36764. function (serieOptions) {
  36765. chart.initSeries(serieOptions);
  36766. });
  36767. chart.linkSeries();
  36768. chart.setSeriesData();
  36769. // Run an event after axes and series are initialized, but before
  36770. // render. At this stage, the series data is indexed and cached in the
  36771. // xData and yData arrays, so we can access those before rendering. Used
  36772. // in Highcharts Stock.
  36773. fireEvent(chart, 'beforeRender');
  36774. chart.render();
  36775. chart.pointer.getChartPosition(); // #14973
  36776. // Fire the load event if there are no external images
  36777. if (!chart.renderer.imgCount && !chart.hasLoaded) {
  36778. chart.onload();
  36779. }
  36780. // If the chart was rendered outside the top container, put it back in
  36781. // (#3679)
  36782. chart.temporaryDisplay(true);
  36783. }
  36784. /**
  36785. * Internal function that runs on chart load, async if any images are loaded
  36786. * in the chart. Runs the callbacks and triggers the `load` and `render`
  36787. * events.
  36788. *
  36789. * @private
  36790. * @function Highcharts.Chart#onload
  36791. * @emits Highcharts.Chart#event:load
  36792. * @emits Highcharts.Chart#event:render
  36793. */
  36794. onload() {
  36795. // Run callbacks, first the ones registered by modules, then user's one
  36796. this.callbacks.concat([this.callback]).forEach(function (fn) {
  36797. // Chart destroyed in its own callback (#3600)
  36798. if (fn && typeof this.index !== 'undefined') {
  36799. fn.apply(this, [this]);
  36800. }
  36801. }, this);
  36802. fireEvent(this, 'load');
  36803. fireEvent(this, 'render');
  36804. // Set up auto resize, check for not destroyed (#6068)
  36805. if (defined(this.index)) {
  36806. this.setReflow();
  36807. }
  36808. this.warnIfA11yModuleNotLoaded();
  36809. // Don't run again
  36810. this.hasLoaded = true;
  36811. }
  36812. /**
  36813. * Emit console warning if the a11y module is not loaded.
  36814. */
  36815. warnIfA11yModuleNotLoaded() {
  36816. const { options, title } = this;
  36817. if (options && !this.accessibility) {
  36818. // Make chart behave as an image with the title as alt text
  36819. this.renderer.boxWrapper.attr({
  36820. role: 'img',
  36821. 'aria-label': ((title && title.element.textContent) || ''
  36822. // #17753, < is not allowed in SVG attributes
  36823. ).replace(/</g, '&lt;')
  36824. });
  36825. if (!(options.accessibility && options.accessibility.enabled === false)) {
  36826. error('Highcharts warning: Consider including the ' +
  36827. '"accessibility.js" module to make your chart more ' +
  36828. 'usable for people with disabilities. Set the ' +
  36829. '"accessibility.enabled" option to false to remove this ' +
  36830. 'warning. See https://www.highcharts.com/docs/accessibility/accessibility-module.', false, this);
  36831. }
  36832. }
  36833. }
  36834. /**
  36835. * Add a series to the chart after render time. Note that this method should
  36836. * never be used when adding data synchronously at chart render time, as it
  36837. * adds expense to the calculations and rendering. When adding data at the
  36838. * same time as the chart is initialized, add the series as a configuration
  36839. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  36840. *
  36841. * @sample highcharts/members/chart-addseries/
  36842. * Add a series from a button
  36843. * @sample stock/members/chart-addseries/
  36844. * Add a series in Highcharts Stock
  36845. *
  36846. * @function Highcharts.Chart#addSeries
  36847. *
  36848. * @param {Highcharts.SeriesOptionsType} options
  36849. * The config options for the series.
  36850. *
  36851. * @param {boolean} [redraw=true]
  36852. * Whether to redraw the chart after adding.
  36853. *
  36854. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36855. * Whether to apply animation, and optionally animation
  36856. * configuration. When `undefined`, it applies the animation that is
  36857. * set in the `chart.animation` option.
  36858. *
  36859. * @return {Highcharts.Series}
  36860. * The newly created series object.
  36861. *
  36862. * @emits Highcharts.Chart#event:addSeries
  36863. * @emits Highcharts.Chart#event:afterAddSeries
  36864. */
  36865. addSeries(options, redraw, animation) {
  36866. const chart = this;
  36867. let series;
  36868. if (options) { // <- not necessary
  36869. redraw = pick(redraw, true); // defaults to true
  36870. fireEvent(chart, 'addSeries', { options: options }, function () {
  36871. series = chart.initSeries(options);
  36872. chart.isDirtyLegend = true;
  36873. chart.linkSeries();
  36874. if (series.enabledDataSorting) {
  36875. // We need to call `setData` after `linkSeries`
  36876. series.setData(options.data, false);
  36877. }
  36878. fireEvent(chart, 'afterAddSeries', { series: series });
  36879. if (redraw) {
  36880. chart.redraw(animation);
  36881. }
  36882. });
  36883. }
  36884. return series;
  36885. }
  36886. /**
  36887. * Add an axis to the chart after render time. Note that this method should
  36888. * never be used when adding data synchronously at chart render time, as it
  36889. * adds expense to the calculations and rendering. When adding data at the
  36890. * same time as the chart is initialized, add the axis as a configuration
  36891. * option instead.
  36892. *
  36893. * @sample highcharts/members/chart-addaxis/
  36894. * Add and remove axes
  36895. *
  36896. * @function Highcharts.Chart#addAxis
  36897. *
  36898. * @param {Highcharts.AxisOptions} options
  36899. * The axis options.
  36900. *
  36901. * @param {boolean} [isX=false]
  36902. * Whether it is an X axis or a value axis.
  36903. *
  36904. * @param {boolean} [redraw=true]
  36905. * Whether to redraw the chart after adding.
  36906. *
  36907. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36908. * Whether and how to apply animation in the redraw. When
  36909. * `undefined`, it applies the animation that is set in the
  36910. * `chart.animation` option.
  36911. *
  36912. * @return {Highcharts.Axis}
  36913. * The newly generated Axis object.
  36914. */
  36915. addAxis(options, isX, redraw, animation) {
  36916. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  36917. }
  36918. /**
  36919. * Add a color axis to the chart after render time. Note that this method
  36920. * should never be used when adding data synchronously at chart render time,
  36921. * as it adds expense to the calculations and rendering. When adding data at
  36922. * the same time as the chart is initialized, add the axis as a
  36923. * configuration option instead.
  36924. *
  36925. * @sample highcharts/members/chart-addaxis/
  36926. * Add and remove axes
  36927. *
  36928. * @function Highcharts.Chart#addColorAxis
  36929. *
  36930. * @param {Highcharts.ColorAxisOptions} options
  36931. * The axis options.
  36932. *
  36933. * @param {boolean} [redraw=true]
  36934. * Whether to redraw the chart after adding.
  36935. *
  36936. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36937. * Whether and how to apply animation in the redraw. When
  36938. * `undefined`, it applies the animation that is set in the
  36939. * `chart.animation` option.
  36940. *
  36941. * @return {Highcharts.Axis}
  36942. * The newly generated Axis object.
  36943. */
  36944. addColorAxis(options, redraw, animation) {
  36945. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  36946. }
  36947. /**
  36948. * Factory for creating different axis types.
  36949. *
  36950. * @private
  36951. * @function Highcharts.Chart#createAxis
  36952. *
  36953. * @param {string} coll
  36954. * An axis type.
  36955. *
  36956. * @param {...Array<*>} arguments
  36957. * All arguments for the constructor.
  36958. *
  36959. * @return {Highcharts.Axis}
  36960. * The newly generated Axis object.
  36961. */
  36962. createAxis(coll, options) {
  36963. const axis = new Axis(this, options.axis, coll);
  36964. if (pick(options.redraw, true)) {
  36965. this.redraw(options.animation);
  36966. }
  36967. return axis;
  36968. }
  36969. /**
  36970. * Dim the chart and show a loading text or symbol. Options for the loading
  36971. * screen are defined in {@link
  36972. * https://api.highcharts.com/highcharts/loading|the loading options}.
  36973. *
  36974. * @sample highcharts/members/chart-hideloading/
  36975. * Show and hide loading from a button
  36976. * @sample highcharts/members/chart-showloading/
  36977. * Apply different text labels
  36978. * @sample stock/members/chart-show-hide-loading/
  36979. * Toggle loading in Highcharts Stock
  36980. *
  36981. * @function Highcharts.Chart#showLoading
  36982. *
  36983. * @param {string} [str]
  36984. * An optional text to show in the loading label instead of the
  36985. * default one. The default text is set in
  36986. * [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
  36987. */
  36988. showLoading(str) {
  36989. const chart = this, options = chart.options, loadingOptions = options.loading, setLoadingSize = function () {
  36990. if (loadingDiv) {
  36991. css(loadingDiv, {
  36992. left: chart.plotLeft + 'px',
  36993. top: chart.plotTop + 'px',
  36994. width: chart.plotWidth + 'px',
  36995. height: chart.plotHeight + 'px'
  36996. });
  36997. }
  36998. };
  36999. let loadingDiv = chart.loadingDiv, loadingSpan = chart.loadingSpan;
  37000. // create the layer at the first call
  37001. if (!loadingDiv) {
  37002. chart.loadingDiv = loadingDiv = createElement('div', {
  37003. className: 'highcharts-loading highcharts-loading-hidden'
  37004. }, null, chart.container);
  37005. }
  37006. if (!loadingSpan) {
  37007. chart.loadingSpan = loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  37008. addEvent(chart, 'redraw', setLoadingSize); // #1080
  37009. }
  37010. loadingDiv.className = 'highcharts-loading';
  37011. // Update text
  37012. AST.setElementHTML(loadingSpan, pick(str, options.lang.loading, ''));
  37013. if (!chart.styledMode) {
  37014. // Update visuals
  37015. css(loadingDiv, extend(loadingOptions.style, {
  37016. zIndex: 10
  37017. }));
  37018. css(loadingSpan, loadingOptions.labelStyle);
  37019. // Show it
  37020. if (!chart.loadingShown) {
  37021. css(loadingDiv, {
  37022. opacity: 0,
  37023. display: ''
  37024. });
  37025. animate(loadingDiv, {
  37026. opacity: loadingOptions.style.opacity || 0.5
  37027. }, {
  37028. duration: loadingOptions.showDuration || 0
  37029. });
  37030. }
  37031. }
  37032. chart.loadingShown = true;
  37033. setLoadingSize();
  37034. }
  37035. /**
  37036. * Hide the loading layer.
  37037. *
  37038. * @see Highcharts.Chart#showLoading
  37039. *
  37040. * @sample highcharts/members/chart-hideloading/
  37041. * Show and hide loading from a button
  37042. * @sample stock/members/chart-show-hide-loading/
  37043. * Toggle loading in Highcharts Stock
  37044. *
  37045. * @function Highcharts.Chart#hideLoading
  37046. */
  37047. hideLoading() {
  37048. const options = this.options, loadingDiv = this.loadingDiv;
  37049. if (loadingDiv) {
  37050. loadingDiv.className =
  37051. 'highcharts-loading highcharts-loading-hidden';
  37052. if (!this.styledMode) {
  37053. animate(loadingDiv, {
  37054. opacity: 0
  37055. }, {
  37056. duration: options.loading.hideDuration || 100,
  37057. complete: function () {
  37058. css(loadingDiv, { display: 'none' });
  37059. }
  37060. });
  37061. }
  37062. }
  37063. this.loadingShown = false;
  37064. }
  37065. /**
  37066. * A generic function to update any element of the chart. Elements can be
  37067. * enabled and disabled, moved, re-styled, re-formatted etc.
  37068. *
  37069. * A special case is configuration objects that take arrays, for example
  37070. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  37071. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  37072. * [series](https://api.highcharts.com/highcharts/series). For these
  37073. * collections, an `id` option is used to map the new option set to an
  37074. * existing object. If an existing object of the same id is not found, the
  37075. * corresponding item is updated. So for example, running `chart.update`
  37076. * with a series item without an id, will cause the existing chart's series
  37077. * with the same index in the series array to be updated. When the
  37078. * `oneToOne` parameter is true, `chart.update` will also take care of
  37079. * adding and removing items from the collection. Read more under the
  37080. * parameter description below.
  37081. *
  37082. * Note that when changing series data, `chart.update` may mutate the passed
  37083. * data options.
  37084. *
  37085. * See also the
  37086. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  37087. * Switching between `responsive.rules` basically runs `chart.update` under
  37088. * the hood.
  37089. *
  37090. * @sample highcharts/members/chart-update/
  37091. * Update chart geometry
  37092. *
  37093. * @function Highcharts.Chart#update
  37094. *
  37095. * @param {Highcharts.Options} options
  37096. * A configuration object for the new chart options.
  37097. *
  37098. * @param {boolean} [redraw=true]
  37099. * Whether to redraw the chart.
  37100. *
  37101. * @param {boolean} [oneToOne=false]
  37102. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  37103. * collections will be updated one to one, and items will be either
  37104. * added or removed to match the new updated options. For example,
  37105. * if the chart has two series and we call `chart.update` with a
  37106. * configuration containing three series, one will be added. If we
  37107. * call `chart.update` with one series, one will be removed. Setting
  37108. * an empty `series` array will remove all series, but leaving out
  37109. * the`series` property will leave all series untouched. If the
  37110. * series have id's, the new series options will be matched by id,
  37111. * and the remaining ones removed.
  37112. *
  37113. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37114. * Whether to apply animation, and optionally animation
  37115. * configuration. When `undefined`, it applies the animation that is
  37116. * set in the `chart.animation` option.
  37117. *
  37118. * @emits Highcharts.Chart#event:update
  37119. * @emits Highcharts.Chart#event:afterUpdate
  37120. */
  37121. update(options, redraw, oneToOne, animation) {
  37122. const chart = this, adders = {
  37123. credits: 'addCredits',
  37124. title: 'setTitle',
  37125. subtitle: 'setSubtitle',
  37126. caption: 'setCaption'
  37127. }, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  37128. let updateAllAxes, updateAllSeries, runSetSize;
  37129. fireEvent(chart, 'update', { options: options });
  37130. // If there are responsive rules in action, undo the responsive rules
  37131. // before we apply the updated options and replay the responsive rules
  37132. // on top from the chart.redraw function (#9617).
  37133. if (!isResponsiveOptions) {
  37134. chart.setResponsive(false, true);
  37135. }
  37136. options = diffObjects(options, chart.options);
  37137. chart.userOptions = merge(chart.userOptions, options);
  37138. // If the top-level chart option is present, some special updates are
  37139. // required
  37140. const optionsChart = options.chart;
  37141. if (optionsChart) {
  37142. merge(true, chart.options.chart, optionsChart);
  37143. // Add support for deprecated zooming options like zoomType, #17861
  37144. this.setZoomOptions();
  37145. // Setter function
  37146. if ('className' in optionsChart) {
  37147. chart.setClassName(optionsChart.className);
  37148. }
  37149. if ('inverted' in optionsChart ||
  37150. 'polar' in optionsChart ||
  37151. 'type' in optionsChart) {
  37152. // Parse options.chart.inverted and options.chart.polar together
  37153. // with the available series.
  37154. chart.propFromSeries();
  37155. updateAllAxes = true;
  37156. }
  37157. if ('alignTicks' in optionsChart) { // #6452
  37158. updateAllAxes = true;
  37159. }
  37160. if ('events' in optionsChart) {
  37161. // Chart event handlers
  37162. registerEventOptions(this, optionsChart);
  37163. }
  37164. objectEach(optionsChart, function (val, key) {
  37165. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  37166. -1) {
  37167. updateAllSeries = true;
  37168. }
  37169. // Only dirty box
  37170. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  37171. chart.isDirtyBox = true;
  37172. }
  37173. // Chart setSize
  37174. if (chart.propsRequireReflow.indexOf(key) !== -1) {
  37175. if (isResponsiveOptions) {
  37176. chart.isDirtyBox = true;
  37177. }
  37178. else {
  37179. runSetSize = true;
  37180. }
  37181. }
  37182. });
  37183. if (!chart.styledMode && optionsChart.style) {
  37184. chart.renderer.setStyle(chart.options.chart.style || {});
  37185. }
  37186. }
  37187. // Moved up, because tooltip needs updated plotOptions (#6218)
  37188. if (!chart.styledMode && options.colors) {
  37189. this.options.colors = options.colors;
  37190. }
  37191. if (options.time) {
  37192. // Maintaining legacy global time. If the chart is instanciated
  37193. // first with global time, then updated with time options, we need
  37194. // to create a new Time instance to avoid mutating the global time
  37195. // (#10536).
  37196. if (this.time === defaultTime) {
  37197. this.time = new Time(options.time);
  37198. }
  37199. // If we're updating, the time class is different from other chart
  37200. // classes (chart.legend, chart.tooltip etc) in that it doesn't know
  37201. // about the chart. The other chart[something].update functions also
  37202. // set the chart.options[something]. For the time class however we
  37203. // need to update the chart options separately. #14230.
  37204. merge(true, chart.options.time, options.time);
  37205. }
  37206. // Some option stuctures correspond one-to-one to chart objects that
  37207. // have update methods, for example
  37208. // options.credits => chart.credits
  37209. // options.legend => chart.legend
  37210. // options.title => chart.title
  37211. // options.tooltip => chart.tooltip
  37212. // options.subtitle => chart.subtitle
  37213. // options.mapNavigation => chart.mapNavigation
  37214. // options.navigator => chart.navigator
  37215. // options.scrollbar => chart.scrollbar
  37216. objectEach(options, function (val, key) {
  37217. if (chart[key] &&
  37218. typeof chart[key].update === 'function') {
  37219. chart[key].update(val, false);
  37220. // If a one-to-one object does not exist, look for an adder function
  37221. }
  37222. else if (typeof chart[adders[key]] === 'function') {
  37223. chart[adders[key]](val);
  37224. // Else, just merge the options. For nodes like loading, noData,
  37225. // plotOptions
  37226. }
  37227. else if (key !== 'colors' &&
  37228. chart.collectionsWithUpdate.indexOf(key) === -1) {
  37229. merge(true, chart.options[key], options[key]);
  37230. }
  37231. if (key !== 'chart' &&
  37232. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  37233. updateAllSeries = true;
  37234. }
  37235. });
  37236. // Setters for collections. For axes and series, each item is referred
  37237. // by an id. If the id is not found, it defaults to the corresponding
  37238. // item in the collection, so setting one series without an id, will
  37239. // update the first series in the chart. Setting two series without
  37240. // an id will update the first and the second respectively (#6019)
  37241. // chart.update and responsive.
  37242. this.collectionsWithUpdate.forEach(function (coll) {
  37243. if (options[coll]) {
  37244. splat(options[coll]).forEach(function (newOptions, i) {
  37245. const hasId = defined(newOptions.id);
  37246. let item;
  37247. // Match by id
  37248. if (hasId) {
  37249. item = chart.get(newOptions.id);
  37250. }
  37251. // No match by id found, match by index instead
  37252. if (!item && chart[coll]) {
  37253. item = chart[coll][pick(newOptions.index, i)];
  37254. // Check if we grabbed an item with an exising but
  37255. // different id (#13541). Check that the item in this
  37256. // position is not internal (navigator).
  37257. if (item && ((hasId && defined(item.options.id)) ||
  37258. item.options.isInternal)) {
  37259. item = void 0;
  37260. }
  37261. }
  37262. if (item && item.coll === coll) {
  37263. item.update(newOptions, false);
  37264. if (oneToOne) {
  37265. item.touched = true;
  37266. }
  37267. }
  37268. // If oneToOne and no matching item is found, add one
  37269. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  37270. chart.collectionsWithInit[coll][0].apply(chart,
  37271. // [newOptions, ...extraArguments, redraw=false]
  37272. [
  37273. newOptions
  37274. ].concat(
  37275. // Not all initializers require extra args
  37276. chart.collectionsWithInit[coll][1] || []).concat([
  37277. false
  37278. ])).touched = true;
  37279. }
  37280. });
  37281. // Add items for removal
  37282. if (oneToOne) {
  37283. chart[coll].forEach(function (item) {
  37284. if (!item.touched && !item.options.isInternal) {
  37285. itemsForRemoval.push(item);
  37286. }
  37287. else {
  37288. delete item.touched;
  37289. }
  37290. });
  37291. }
  37292. }
  37293. });
  37294. itemsForRemoval.forEach(function (item) {
  37295. if (item.chart && item.remove) { // #9097, avoid removing twice
  37296. item.remove(false);
  37297. }
  37298. });
  37299. if (updateAllAxes) {
  37300. chart.axes.forEach(function (axis) {
  37301. axis.update({}, false);
  37302. });
  37303. }
  37304. // Certain options require the whole series structure to be thrown away
  37305. // and rebuilt
  37306. if (updateAllSeries) {
  37307. chart.getSeriesOrderByLinks().forEach(function (series) {
  37308. // Avoid removed navigator series
  37309. if (series.chart) {
  37310. series.update({}, false);
  37311. }
  37312. }, this);
  37313. }
  37314. // Update size. Redraw is forced.
  37315. const newWidth = optionsChart && optionsChart.width;
  37316. const newHeight = optionsChart && (isString(optionsChart.height) ?
  37317. relativeLength(optionsChart.height, newWidth || chart.chartWidth) :
  37318. optionsChart.height);
  37319. if (
  37320. // In this case, run chart.setSize with newWidth and newHeight which
  37321. // are undefined, only for reflowing chart elements because margin
  37322. // or spacing has been set (#8190)
  37323. runSetSize ||
  37324. // In this case, the size is actually set
  37325. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  37326. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  37327. chart.setSize(newWidth, newHeight, animation);
  37328. }
  37329. else if (pick(redraw, true)) {
  37330. chart.redraw(animation);
  37331. }
  37332. fireEvent(chart, 'afterUpdate', {
  37333. options: options,
  37334. redraw: redraw,
  37335. animation: animation
  37336. });
  37337. }
  37338. /**
  37339. * Shortcut to set the subtitle options. This can also be done from {@link
  37340. * Chart#update} or {@link Chart#setTitle}.
  37341. *
  37342. * @function Highcharts.Chart#setSubtitle
  37343. *
  37344. * @param {Highcharts.SubtitleOptions} options
  37345. * New subtitle options. The subtitle text itself is set by the
  37346. * `options.text` property.
  37347. */
  37348. setSubtitle(options, redraw) {
  37349. this.applyDescription('subtitle', options);
  37350. this.layOutTitles(redraw);
  37351. }
  37352. /**
  37353. * Set the caption options. This can also be done from {@link
  37354. * Chart#update}.
  37355. *
  37356. * @function Highcharts.Chart#setCaption
  37357. *
  37358. * @param {Highcharts.CaptionOptions} options
  37359. * New caption options. The caption text itself is set by the
  37360. * `options.text` property.
  37361. */
  37362. setCaption(options, redraw) {
  37363. this.applyDescription('caption', options);
  37364. this.layOutTitles(redraw);
  37365. }
  37366. /**
  37367. * Display the zoom button, so users can reset zoom to the default view
  37368. * settings.
  37369. *
  37370. * @function Highcharts.Chart#showResetZoom
  37371. *
  37372. * @emits Highcharts.Chart#event:afterShowResetZoom
  37373. * @emits Highcharts.Chart#event:beforeShowResetZoom
  37374. */
  37375. showResetZoom() {
  37376. const chart = this, lang = defaultOptions.lang, btnOptions = chart.zooming.resetButton, theme = btnOptions.theme, alignTo = (btnOptions.relativeTo === 'chart' ||
  37377. btnOptions.relativeTo === 'spacingBox' ?
  37378. null :
  37379. 'scrollablePlotBox');
  37380. /**
  37381. * @private
  37382. */
  37383. function zoomOut() {
  37384. chart.zoomOut();
  37385. }
  37386. fireEvent(this, 'beforeShowResetZoom', null, function () {
  37387. chart.resetZoomButton = chart.renderer
  37388. .button(lang.resetZoom, null, null, zoomOut, theme)
  37389. .attr({
  37390. align: btnOptions.position.align,
  37391. title: lang.resetZoomTitle
  37392. })
  37393. .addClass('highcharts-reset-zoom')
  37394. .add()
  37395. .align(btnOptions.position, false, alignTo);
  37396. });
  37397. fireEvent(this, 'afterShowResetZoom');
  37398. }
  37399. /**
  37400. * Zoom the chart out after a user has zoomed in. See also
  37401. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  37402. *
  37403. * @function Highcharts.Chart#zoomOut
  37404. *
  37405. * @emits Highcharts.Chart#event:selection
  37406. */
  37407. zoomOut() {
  37408. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  37409. }
  37410. /**
  37411. * Zoom into a given portion of the chart given by axis coordinates.
  37412. *
  37413. * @private
  37414. * @function Highcharts.Chart#zoom
  37415. * @param {Highcharts.SelectEventObject} event
  37416. */
  37417. zoom(event) {
  37418. const chart = this, pointer = chart.pointer;
  37419. let displayButton = false, hasZoomed;
  37420. // If zoom is called with no arguments, reset the axes
  37421. if (!event || event.resetSelection) {
  37422. chart.axes.forEach(function (axis) {
  37423. hasZoomed = axis.zoom();
  37424. });
  37425. pointer.initiated = false; // #6804
  37426. }
  37427. else { // else, zoom in on all axes
  37428. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  37429. const axis = axisData.axis, isXAxis = axis.isXAxis;
  37430. // don't zoom more than minRange
  37431. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] &&
  37432. (defined(pointer.mouseDownX) &&
  37433. defined(pointer.mouseDownY) &&
  37434. chart.isInsidePlot(pointer.mouseDownX - chart.plotLeft, pointer.mouseDownY - chart.plotTop, { axis })) || !defined(chart.inverted ? pointer.mouseDownX : pointer.mouseDownY)) {
  37435. hasZoomed = axis.zoom(axisData.min, axisData.max);
  37436. if (axis.displayBtn) {
  37437. displayButton = true;
  37438. }
  37439. }
  37440. });
  37441. }
  37442. // Show or hide the Reset zoom button
  37443. const resetZoomButton = chart.resetZoomButton;
  37444. if (displayButton && !resetZoomButton) {
  37445. chart.showResetZoom();
  37446. }
  37447. else if (!displayButton && isObject(resetZoomButton)) {
  37448. chart.resetZoomButton = resetZoomButton.destroy();
  37449. }
  37450. // Redraw
  37451. if (hasZoomed) {
  37452. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  37453. }
  37454. }
  37455. /**
  37456. * Pan the chart by dragging the mouse across the pane. This function is
  37457. * called on mouse move, and the distance to pan is computed from chartX
  37458. * compared to the first chartX position in the dragging operation.
  37459. *
  37460. * @private
  37461. * @function Highcharts.Chart#pan
  37462. * @param {Highcharts.PointerEventObject} e
  37463. * @param {string} panning
  37464. */
  37465. pan(e, panning) {
  37466. const chart = this, hoverPoints = chart.hoverPoints, panningOptions = (typeof panning === 'object' ?
  37467. panning :
  37468. {
  37469. enabled: panning,
  37470. type: 'x'
  37471. }), chartOptions = chart.options.chart;
  37472. if (chartOptions && chartOptions.panning) {
  37473. chartOptions.panning = panningOptions;
  37474. }
  37475. const type = panningOptions.type;
  37476. let doRedraw;
  37477. fireEvent(this, 'pan', { originalEvent: e }, function () {
  37478. // remove active points for shared tooltip
  37479. if (hoverPoints) {
  37480. hoverPoints.forEach(function (point) {
  37481. point.setState();
  37482. });
  37483. }
  37484. let axes = chart.xAxis;
  37485. if (type === 'xy') {
  37486. axes = axes.concat(chart.yAxis);
  37487. }
  37488. else if (type === 'y') {
  37489. axes = chart.yAxis;
  37490. }
  37491. const nextMousePos = {};
  37492. axes.forEach(function (axis) {
  37493. if (!axis.options.panningEnabled || axis.options.isInternal) {
  37494. return;
  37495. }
  37496. const horiz = axis.horiz, mousePos = e[horiz ? 'chartX' : 'chartY'], mouseDown = horiz ? 'mouseDownX' : 'mouseDownY', startPos = chart[mouseDown], halfPointRange = axis.minPointOffset || 0, pointRangeDirection = (axis.reversed && !chart.inverted) ||
  37497. (!axis.reversed && chart.inverted) ?
  37498. -1 :
  37499. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  37500. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  37501. ((halfPointRange * pointRangeDirection) ||
  37502. (axis.isXAxis && axis.pointRangePadding) ||
  37503. 0), flipped = panMax < panMin, hasVerticalPanning = axis.hasVerticalPanning();
  37504. let newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, panningState = axis.panningState, spill;
  37505. // General calculations of panning state.
  37506. // This is related to using vertical panning. (#11315).
  37507. if (hasVerticalPanning &&
  37508. !axis.isXAxis && (!panningState || panningState.isDirty)) {
  37509. axis.series.forEach(function (series) {
  37510. const processedData = series.getProcessedData(true), dataExtremes = series.getExtremes(processedData.yData, true);
  37511. if (!panningState) {
  37512. panningState = {
  37513. startMin: Number.MAX_VALUE,
  37514. startMax: -Number.MAX_VALUE
  37515. };
  37516. }
  37517. if (isNumber(dataExtremes.dataMin) &&
  37518. isNumber(dataExtremes.dataMax)) {
  37519. panningState.startMin = Math.min(pick(series.options.threshold, Infinity), dataExtremes.dataMin, panningState.startMin);
  37520. panningState.startMax = Math.max(pick(series.options.threshold, -Infinity), dataExtremes.dataMax, panningState.startMax);
  37521. }
  37522. });
  37523. }
  37524. const paddedMin = Math.min(pick(panningState && panningState.startMin, extremes.dataMin), halfPointRange ?
  37525. extremes.min :
  37526. axis.toValue(axis.toPixels(extremes.min) -
  37527. axis.minPixelPadding));
  37528. const paddedMax = Math.max(pick(panningState && panningState.startMax, extremes.dataMax), halfPointRange ?
  37529. extremes.max :
  37530. axis.toValue(axis.toPixels(extremes.max) +
  37531. axis.minPixelPadding));
  37532. axis.panningState = panningState;
  37533. // It is not necessary to calculate extremes on ordinal axis,
  37534. // because they are already calculated, so we don't want to
  37535. // override them.
  37536. if (!axis.isOrdinal) {
  37537. // If the new range spills over, either to the min or max,
  37538. // adjust the new range.
  37539. spill = paddedMin - newMin;
  37540. if (spill > 0) {
  37541. newMax += spill;
  37542. newMin = paddedMin;
  37543. }
  37544. spill = newMax - paddedMax;
  37545. if (spill > 0) {
  37546. newMax = paddedMax;
  37547. newMin -= spill;
  37548. }
  37549. // Set new extremes if they are actually new
  37550. if (axis.series.length &&
  37551. newMin !== extremes.min &&
  37552. newMax !== extremes.max &&
  37553. newMin >= paddedMin &&
  37554. newMax <= paddedMax) {
  37555. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  37556. if (!chart.resetZoomButton &&
  37557. // Show reset zoom button only when both newMin and
  37558. // newMax values are between padded axis range.
  37559. newMin !== paddedMin &&
  37560. newMax !== paddedMax &&
  37561. type.match('y')) {
  37562. chart.showResetZoom();
  37563. axis.displayBtn = false;
  37564. }
  37565. doRedraw = true;
  37566. }
  37567. // set new reference for next run:
  37568. nextMousePos[mouseDown] = mousePos;
  37569. }
  37570. });
  37571. objectEach(nextMousePos, (pos, down) => {
  37572. chart[down] = pos;
  37573. });
  37574. if (doRedraw) {
  37575. chart.redraw(false);
  37576. }
  37577. css(chart.container, { cursor: 'move' });
  37578. });
  37579. }
  37580. }
  37581. extend(Chart.prototype, {
  37582. // Hook for adding callbacks in modules
  37583. callbacks: [],
  37584. /**
  37585. * These collections (arrays) implement `Chart.addSomethig` method used in
  37586. * chart.update() to create new object in the collection. Equivalent for
  37587. * deleting is resolved by simple `Somethig.remove()`.
  37588. *
  37589. * Note: We need to define these references after initializers are bound to
  37590. * chart's prototype.
  37591. *
  37592. * @private
  37593. */
  37594. collectionsWithInit: {
  37595. // collectionName: [ initializingMethod, [extraArguments] ]
  37596. xAxis: [Chart.prototype.addAxis, [true]],
  37597. yAxis: [Chart.prototype.addAxis, [false]],
  37598. series: [Chart.prototype.addSeries]
  37599. },
  37600. /**
  37601. * These collections (arrays) implement update() methods with support for
  37602. * one-to-one option.
  37603. * @private
  37604. */
  37605. collectionsWithUpdate: [
  37606. 'xAxis',
  37607. 'yAxis',
  37608. 'series'
  37609. ],
  37610. /**
  37611. * These properties cause isDirtyBox to be set to true when updating. Can be
  37612. * extended from plugins.
  37613. * @private
  37614. */
  37615. propsRequireDirtyBox: [
  37616. 'backgroundColor',
  37617. 'borderColor',
  37618. 'borderWidth',
  37619. 'borderRadius',
  37620. 'plotBackgroundColor',
  37621. 'plotBackgroundImage',
  37622. 'plotBorderColor',
  37623. 'plotBorderWidth',
  37624. 'plotShadow',
  37625. 'shadow'
  37626. ],
  37627. /**
  37628. * These properties require a full reflow of chart elements, best
  37629. * implemented through running `Chart.setSize` internally (#8190).
  37630. * @private
  37631. */
  37632. propsRequireReflow: [
  37633. 'margin',
  37634. 'marginTop',
  37635. 'marginRight',
  37636. 'marginBottom',
  37637. 'marginLeft',
  37638. 'spacing',
  37639. 'spacingTop',
  37640. 'spacingRight',
  37641. 'spacingBottom',
  37642. 'spacingLeft'
  37643. ],
  37644. /**
  37645. * These properties cause all series to be updated when updating. Can be
  37646. * extended from plugins.
  37647. * @private
  37648. */
  37649. propsRequireUpdateSeries: [
  37650. 'chart.inverted',
  37651. 'chart.polar',
  37652. 'chart.ignoreHiddenSeries',
  37653. 'chart.type',
  37654. 'colors',
  37655. 'plotOptions',
  37656. 'time',
  37657. 'tooltip'
  37658. ]
  37659. });
  37660. /* *
  37661. *
  37662. * Default Export
  37663. *
  37664. * */
  37665. /* *
  37666. *
  37667. * API Declarations
  37668. *
  37669. * */
  37670. /**
  37671. * Callback for chart constructors.
  37672. *
  37673. * @callback Highcharts.ChartCallbackFunction
  37674. *
  37675. * @param {Highcharts.Chart} chart
  37676. * Created chart.
  37677. */
  37678. /**
  37679. * Format a number and return a string based on input settings.
  37680. *
  37681. * @callback Highcharts.NumberFormatterCallbackFunction
  37682. *
  37683. * @param {number} number
  37684. * The input number to format.
  37685. *
  37686. * @param {number} decimals
  37687. * The amount of decimals. A value of -1 preserves the amount in the
  37688. * input number.
  37689. *
  37690. * @param {string} [decimalPoint]
  37691. * The decimal point, defaults to the one given in the lang options, or
  37692. * a dot.
  37693. *
  37694. * @param {string} [thousandsSep]
  37695. * The thousands separator, defaults to the one given in the lang
  37696. * options, or a space character.
  37697. *
  37698. * @return {string} The formatted number.
  37699. */
  37700. /**
  37701. * The chart title. The title has an `update` method that allows modifying the
  37702. * options directly or indirectly via `chart.update`.
  37703. *
  37704. * @interface Highcharts.TitleObject
  37705. * @extends Highcharts.SVGElement
  37706. */ /**
  37707. * Modify options for the title.
  37708. *
  37709. * @function Highcharts.TitleObject#update
  37710. *
  37711. * @param {Highcharts.TitleOptions} titleOptions
  37712. * Options to modify.
  37713. *
  37714. * @param {boolean} [redraw=true]
  37715. * Whether to redraw the chart after the title is altered. If doing more
  37716. * operations on the chart, it is a good idea to set redraw to false and
  37717. * call {@link Chart#redraw} after.
  37718. */
  37719. /**
  37720. * The chart subtitle. The subtitle has an `update` method that
  37721. * allows modifying the options directly or indirectly via
  37722. * `chart.update`.
  37723. *
  37724. * @interface Highcharts.SubtitleObject
  37725. * @extends Highcharts.SVGElement
  37726. */ /**
  37727. * Modify options for the subtitle.
  37728. *
  37729. * @function Highcharts.SubtitleObject#update
  37730. *
  37731. * @param {Highcharts.SubtitleOptions} subtitleOptions
  37732. * Options to modify.
  37733. *
  37734. * @param {boolean} [redraw=true]
  37735. * Whether to redraw the chart after the subtitle is altered. If doing
  37736. * more operations on the chart, it is a good idea to set redraw to false
  37737. * and call {@link Chart#redraw} after.
  37738. */
  37739. /**
  37740. * The chart caption. The caption has an `update` method that
  37741. * allows modifying the options directly or indirectly via
  37742. * `chart.update`.
  37743. *
  37744. * @interface Highcharts.CaptionObject
  37745. * @extends Highcharts.SVGElement
  37746. */ /**
  37747. * Modify options for the caption.
  37748. *
  37749. * @function Highcharts.CaptionObject#update
  37750. *
  37751. * @param {Highcharts.CaptionOptions} captionOptions
  37752. * Options to modify.
  37753. *
  37754. * @param {boolean} [redraw=true]
  37755. * Whether to redraw the chart after the caption is altered. If doing
  37756. * more operations on the chart, it is a good idea to set redraw to false
  37757. * and call {@link Chart#redraw} after.
  37758. */
  37759. /**
  37760. * @interface Highcharts.ChartIsInsideOptionsObject
  37761. */ /**
  37762. * @name Highcharts.ChartIsInsideOptionsObject#axis
  37763. * @type {Highcharts.Axis|undefined}
  37764. */ /**
  37765. * @name Highcharts.ChartIsInsideOptionsObject#ignoreX
  37766. * @type {boolean|undefined}
  37767. */ /**
  37768. * @name Highcharts.ChartIsInsideOptionsObject#ignoreY
  37769. * @type {boolean|undefined}
  37770. */ /**
  37771. * @name Highcharts.ChartIsInsideOptionsObject#inverted
  37772. * @type {boolean|undefined}
  37773. */ /**
  37774. * @name Highcharts.ChartIsInsideOptionsObject#paneCoordinates
  37775. * @type {boolean|undefined}
  37776. */ /**
  37777. * @name Highcharts.ChartIsInsideOptionsObject#series
  37778. * @type {Highcharts.Series|undefined}
  37779. */ /**
  37780. * @name Highcharts.ChartIsInsideOptionsObject#visiblePlotOnly
  37781. * @type {boolean|undefined}
  37782. */
  37783. ''; // keeps doclets above in JS file
  37784. return Chart;
  37785. });
  37786. _registerModule(_modules, 'Extensions/ScrollablePlotArea.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (A, Axis, Chart, Series, RendererRegistry, U) {
  37787. /* *
  37788. *
  37789. * (c) 2010-2021 Torstein Honsi
  37790. *
  37791. * License: www.highcharts.com/license
  37792. *
  37793. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37794. *
  37795. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  37796. * horizontally on mobile devices. Supports left and right side axes.
  37797. */
  37798. /*
  37799. WIP on vertical scrollable plot area (#9378). To do:
  37800. - Bottom axis positioning
  37801. - Test with Gantt
  37802. - Look for size optimizing the code
  37803. - API and demos
  37804. */
  37805. const { stop } = A;
  37806. const { addEvent, createElement, defined, merge, pick } = U;
  37807. /* eslint-disable no-invalid-this, valid-jsdoc */
  37808. addEvent(Chart, 'afterSetChartSize', function (e) {
  37809. let scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
  37810. if (!this.renderer.forExport) {
  37811. // The amount of pixels to scroll, the difference between chart
  37812. // width and scrollable width
  37813. if (scrollableMinWidth) {
  37814. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  37815. if (scrollablePixelsX) {
  37816. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37817. this.plotBox.width = this.plotWidth += scrollablePixelsX;
  37818. if (this.inverted) {
  37819. this.clipBox.height += scrollablePixelsX;
  37820. }
  37821. else {
  37822. this.clipBox.width += scrollablePixelsX;
  37823. }
  37824. corrections = {
  37825. // Corrections for right side
  37826. 1: { name: 'right', value: scrollablePixelsX }
  37827. };
  37828. }
  37829. // Currently we can only do either X or Y
  37830. }
  37831. else if (scrollableMinHeight) {
  37832. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  37833. if (defined(scrollablePixelsY)) {
  37834. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37835. this.plotBox.height = this.plotHeight += scrollablePixelsY;
  37836. if (this.inverted) {
  37837. this.clipBox.width += scrollablePixelsY;
  37838. }
  37839. else {
  37840. this.clipBox.height += scrollablePixelsY;
  37841. }
  37842. corrections = {
  37843. 2: { name: 'bottom', value: scrollablePixelsY }
  37844. };
  37845. }
  37846. }
  37847. if (corrections && !e.skipAxes) {
  37848. this.axes.forEach(function (axis) {
  37849. // For right and bottom axes, only fix the plot line length
  37850. if (corrections[axis.side]) {
  37851. // Get the plot lines right in getPlotLinePath,
  37852. // temporarily set it to the adjusted plot width.
  37853. axis.getPlotLinePath = function () {
  37854. let marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
  37855. // axis.right or axis.bottom
  37856. margin = this[marginName], path;
  37857. // Temporarily adjust
  37858. this[marginName] = margin - correctionValue;
  37859. path = Axis.prototype.getPlotLinePath.apply(this, arguments);
  37860. // Reset
  37861. this[marginName] = margin;
  37862. return path;
  37863. };
  37864. }
  37865. else {
  37866. // Apply the corrected plotWidth
  37867. axis.setAxisSize();
  37868. axis.setAxisTranslation();
  37869. }
  37870. });
  37871. }
  37872. }
  37873. });
  37874. addEvent(Chart, 'render', function () {
  37875. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  37876. if (this.setUpScrolling) {
  37877. this.setUpScrolling();
  37878. }
  37879. this.applyFixed();
  37880. }
  37881. else if (this.fixedDiv) { // Has been in scrollable mode
  37882. this.applyFixed();
  37883. }
  37884. });
  37885. /**
  37886. * @private
  37887. * @function Highcharts.Chart#setUpScrolling
  37888. * @return {void}
  37889. */
  37890. Chart.prototype.setUpScrolling = function () {
  37891. const css = {
  37892. WebkitOverflowScrolling: 'touch',
  37893. overflowX: 'hidden',
  37894. overflowY: 'hidden'
  37895. };
  37896. if (this.scrollablePixelsX) {
  37897. css.overflowX = 'auto';
  37898. }
  37899. if (this.scrollablePixelsY) {
  37900. css.overflowY = 'auto';
  37901. }
  37902. // Insert a container with position relative
  37903. // that scrolling and fixed container renders to (#10555)
  37904. this.scrollingParent = createElement('div', {
  37905. className: 'highcharts-scrolling-parent'
  37906. }, {
  37907. position: 'relative'
  37908. }, this.renderTo);
  37909. // Add the necessary divs to provide scrolling
  37910. this.scrollingContainer = createElement('div', {
  37911. 'className': 'highcharts-scrolling'
  37912. }, css, this.scrollingParent);
  37913. // On scroll, reset the chart position because it applies to the scrolled
  37914. // container
  37915. let lastHoverPoint;
  37916. addEvent(this.scrollingContainer, 'scroll', () => {
  37917. if (this.pointer) {
  37918. delete this.pointer.chartPosition;
  37919. if (this.hoverPoint) {
  37920. lastHoverPoint = this.hoverPoint;
  37921. }
  37922. this.pointer.runPointActions(void 0, lastHoverPoint, true);
  37923. }
  37924. });
  37925. this.innerContainer = createElement('div', {
  37926. 'className': 'highcharts-inner-container'
  37927. }, null, this.scrollingContainer);
  37928. // Now move the container inside
  37929. this.innerContainer.appendChild(this.container);
  37930. // Don't run again
  37931. this.setUpScrolling = null;
  37932. };
  37933. /**
  37934. * These elements are moved over to the fixed renderer and stay fixed when the
  37935. * user scrolls the chart
  37936. * @private
  37937. */
  37938. Chart.prototype.moveFixedElements = function () {
  37939. let container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
  37940. '.highcharts-breadcrumbs-group',
  37941. '.highcharts-contextbutton',
  37942. '.highcharts-credits',
  37943. '.highcharts-legend',
  37944. '.highcharts-legend-checkbox',
  37945. '.highcharts-navigator-series',
  37946. '.highcharts-navigator-xaxis',
  37947. '.highcharts-navigator-yaxis',
  37948. '.highcharts-navigator',
  37949. '.highcharts-reset-zoom',
  37950. '.highcharts-drillup-button',
  37951. '.highcharts-scrollbar',
  37952. '.highcharts-subtitle',
  37953. '.highcharts-title'
  37954. ], axisClass;
  37955. if (this.scrollablePixelsX && !this.inverted) {
  37956. axisClass = '.highcharts-yaxis';
  37957. }
  37958. else if (this.scrollablePixelsX && this.inverted) {
  37959. axisClass = '.highcharts-xaxis';
  37960. }
  37961. else if (this.scrollablePixelsY && !this.inverted) {
  37962. axisClass = '.highcharts-xaxis';
  37963. }
  37964. else if (this.scrollablePixelsY && this.inverted) {
  37965. axisClass = '.highcharts-yaxis';
  37966. }
  37967. if (axisClass) {
  37968. fixedSelectors.push(`${axisClass}:not(.highcharts-radial-axis)`, `${axisClass}-labels:not(.highcharts-radial-axis-labels)`);
  37969. }
  37970. fixedSelectors.forEach(function (className) {
  37971. [].forEach.call(container.querySelectorAll(className), function (elem) {
  37972. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  37973. fixedRenderer.box :
  37974. fixedRenderer.box.parentNode).appendChild(elem);
  37975. elem.style.pointerEvents = 'auto';
  37976. });
  37977. });
  37978. };
  37979. /**
  37980. * @private
  37981. * @function Highcharts.Chart#applyFixed
  37982. * @return {void}
  37983. */
  37984. Chart.prototype.applyFixed = function () {
  37985. const firstTime = !this.fixedDiv, chartOptions = this.options.chart, scrollableOptions = chartOptions.scrollablePlotArea, Renderer = RendererRegistry.getRendererType();
  37986. let fixedRenderer, scrollableWidth, scrollableHeight;
  37987. // First render
  37988. if (firstTime) {
  37989. this.fixedDiv = createElement('div', {
  37990. className: 'highcharts-fixed'
  37991. }, {
  37992. position: 'absolute',
  37993. overflow: 'hidden',
  37994. pointerEvents: 'none',
  37995. zIndex: (chartOptions.style && chartOptions.style.zIndex || 0) + 2,
  37996. top: 0
  37997. }, null, true);
  37998. if (this.scrollingContainer) {
  37999. this.scrollingContainer.parentNode.insertBefore(this.fixedDiv, this.scrollingContainer);
  38000. }
  38001. this.renderTo.style.overflow = 'visible';
  38002. this.fixedRenderer = fixedRenderer = new Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, this.options.chart.style);
  38003. // Mask
  38004. this.scrollableMask = fixedRenderer
  38005. .path()
  38006. .attr({
  38007. fill: this.options.chart.backgroundColor || '#fff',
  38008. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  38009. zIndex: -1
  38010. })
  38011. .addClass('highcharts-scrollable-mask')
  38012. .add();
  38013. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  38014. addEvent(this, 'afterApplyDrilldown', this.moveFixedElements);
  38015. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  38016. }
  38017. else {
  38018. // Set the size of the fixed renderer to the visible width
  38019. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  38020. }
  38021. if (this.scrollableDirty || firstTime) {
  38022. this.scrollableDirty = false;
  38023. this.moveFixedElements();
  38024. }
  38025. // Increase the size of the scrollable renderer and background
  38026. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  38027. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  38028. stop(this.container);
  38029. this.container.style.width = scrollableWidth + 'px';
  38030. this.container.style.height = scrollableHeight + 'px';
  38031. this.renderer.boxWrapper.attr({
  38032. width: scrollableWidth,
  38033. height: scrollableHeight,
  38034. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  38035. });
  38036. this.chartBackground.attr({
  38037. width: scrollableWidth,
  38038. height: scrollableHeight
  38039. });
  38040. this.scrollingContainer.style.height = this.chartHeight + 'px';
  38041. // Set scroll position
  38042. if (firstTime) {
  38043. if (scrollableOptions.scrollPositionX) {
  38044. this.scrollingContainer.scrollLeft =
  38045. this.scrollablePixelsX *
  38046. scrollableOptions.scrollPositionX;
  38047. }
  38048. if (scrollableOptions.scrollPositionY) {
  38049. this.scrollingContainer.scrollTop =
  38050. this.scrollablePixelsY *
  38051. scrollableOptions.scrollPositionY;
  38052. }
  38053. }
  38054. // Mask behind the left and right side
  38055. let axisOffset = this.axisOffset, maskTop = this.plotTop - axisOffset[0] - 1, maskLeft = this.plotLeft - axisOffset[3] - 1, maskBottom = this.plotTop + this.plotHeight + axisOffset[2] + 1, maskRight = this.plotLeft + this.plotWidth + axisOffset[1] + 1, maskPlotRight = this.plotLeft + this.plotWidth -
  38056. (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
  38057. (this.scrollablePixelsY || 0), d;
  38058. if (this.scrollablePixelsX) {
  38059. d = [
  38060. // Left side
  38061. ['M', 0, maskTop],
  38062. ['L', this.plotLeft - 1, maskTop],
  38063. ['L', this.plotLeft - 1, maskBottom],
  38064. ['L', 0, maskBottom],
  38065. ['Z'],
  38066. // Right side
  38067. ['M', maskPlotRight, maskTop],
  38068. ['L', this.chartWidth, maskTop],
  38069. ['L', this.chartWidth, maskBottom],
  38070. ['L', maskPlotRight, maskBottom],
  38071. ['Z']
  38072. ];
  38073. }
  38074. else if (this.scrollablePixelsY) {
  38075. d = [
  38076. // Top side
  38077. ['M', maskLeft, 0],
  38078. ['L', maskLeft, this.plotTop - 1],
  38079. ['L', maskRight, this.plotTop - 1],
  38080. ['L', maskRight, 0],
  38081. ['Z'],
  38082. // Bottom side
  38083. ['M', maskLeft, maskPlotBottom],
  38084. ['L', maskLeft, this.chartHeight],
  38085. ['L', maskRight, this.chartHeight],
  38086. ['L', maskRight, maskPlotBottom],
  38087. ['Z']
  38088. ];
  38089. }
  38090. else {
  38091. d = [['M', 0, 0]];
  38092. }
  38093. if (this.redrawTrigger !== 'adjustHeight') {
  38094. this.scrollableMask.attr({ d });
  38095. }
  38096. };
  38097. addEvent(Axis, 'afterInit', function () {
  38098. this.chart.scrollableDirty = true;
  38099. });
  38100. addEvent(Series, 'show', function () {
  38101. this.chart.scrollableDirty = true;
  38102. });
  38103. /* *
  38104. *
  38105. * API Declarations
  38106. *
  38107. * */
  38108. /**
  38109. * Options for a scrollable plot area. This feature provides a minimum size for
  38110. * the plot area of the chart. If the size gets smaller than this, typically
  38111. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  38112. * provides smooth scrolling for the contents of the plot area, whereas the
  38113. * title, legend and unaffected axes are fixed.
  38114. *
  38115. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  38116. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  38117. * option is set.
  38118. *
  38119. * @sample highcharts/chart/scrollable-plotarea
  38120. * Scrollable plot area
  38121. * @sample highcharts/chart/scrollable-plotarea-vertical
  38122. * Vertically scrollable plot area
  38123. * @sample {gantt} gantt/chart/scrollable-plotarea-vertical
  38124. * Gantt chart with vertically scrollable plot area
  38125. *
  38126. * @since 6.1.0
  38127. * @product highcharts gantt
  38128. * @apioption chart.scrollablePlotArea
  38129. */
  38130. /**
  38131. * The minimum height for the plot area. If it gets smaller than this, the plot
  38132. * area will become scrollable.
  38133. *
  38134. * @type {number}
  38135. * @since 7.1.2
  38136. * @apioption chart.scrollablePlotArea.minHeight
  38137. */
  38138. /**
  38139. * The minimum width for the plot area. If it gets smaller than this, the plot
  38140. * area will become scrollable.
  38141. *
  38142. * @type {number}
  38143. * @since 6.1.0
  38144. * @apioption chart.scrollablePlotArea.minWidth
  38145. */
  38146. /**
  38147. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38148. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  38149. * Typically we would use 1 if the chart has right aligned Y axes.
  38150. *
  38151. * @type {number}
  38152. * @since 6.1.0
  38153. * @apioption chart.scrollablePlotArea.scrollPositionX
  38154. */
  38155. /**
  38156. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38157. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  38158. *
  38159. * @type {number}
  38160. * @since 7.1.2
  38161. * @apioption chart.scrollablePlotArea.scrollPositionY
  38162. */
  38163. /**
  38164. * The opacity of mask applied on one of the sides of the plot
  38165. * area.
  38166. *
  38167. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  38168. * Disabled opacity for the mask
  38169. *
  38170. * @type {number}
  38171. * @default 0.85
  38172. * @since 7.1.1
  38173. * @apioption chart.scrollablePlotArea.opacity
  38174. */
  38175. (''); // keep doclets above in transpiled file
  38176. });
  38177. _registerModule(_modules, 'Core/Axis/Stacking/StackItem.js', [_modules['Core/Templating.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (T, SeriesRegistry, U) {
  38178. /* *
  38179. *
  38180. * (c) 2010-2021 Torstein Honsi
  38181. *
  38182. * License: www.highcharts.com/license
  38183. *
  38184. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38185. *
  38186. * */
  38187. const { format } = T;
  38188. const { series: Series } = SeriesRegistry;
  38189. const { destroyObjectProperties, fireEvent, isNumber, merge, pick } = U;
  38190. /* *
  38191. *
  38192. * Class
  38193. *
  38194. * */
  38195. /**
  38196. * The class for stacks. Each stack, on a specific X value and either negative
  38197. * or positive, has its own stack item.
  38198. * @private
  38199. */
  38200. class StackItem {
  38201. /* *
  38202. *
  38203. * Constructor
  38204. *
  38205. * */
  38206. constructor(axis, options, negativeValue, x, stackOption) {
  38207. const inverted = axis.chart.inverted, reversed = axis.reversed;
  38208. this.axis = axis;
  38209. // The stack goes to the left either if the stack has negative value
  38210. // or when axis is reversed. XOR operator.
  38211. const isNegative = (this.isNegative = !!negativeValue !== !!reversed);
  38212. // Save the options to be able to style the label
  38213. this.options = options = options || {};
  38214. // Save the x value to be able to position the label later
  38215. this.x = x;
  38216. // Initialize total value
  38217. this.total = null;
  38218. this.cumulative = null;
  38219. // This will keep each points' extremes stored by series.index and point
  38220. // index
  38221. this.points = {};
  38222. this.hasValidPoints = false;
  38223. // Save the stack option on the series configuration object,
  38224. // and whether to treat it as percent
  38225. this.stack = stackOption;
  38226. this.leftCliff = 0;
  38227. this.rightCliff = 0;
  38228. // The align options and text align varies on whether the stack is
  38229. // negative and if the chart is inverted or not.
  38230. // First test the user supplied value, then use the dynamic.
  38231. this.alignOptions = {
  38232. align: options.align ||
  38233. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  38234. verticalAlign: options.verticalAlign ||
  38235. (inverted ? 'middle' : isNegative ? 'bottom' : 'top'),
  38236. y: options.y,
  38237. x: options.x
  38238. };
  38239. this.textAlign =
  38240. options.textAlign ||
  38241. (inverted ? (!isNegative ? 'left' : 'right') : 'center');
  38242. }
  38243. /**
  38244. * @private
  38245. */
  38246. destroy() {
  38247. destroyObjectProperties(this, this.axis);
  38248. }
  38249. /**
  38250. * Renders the stack total label and adds it to the stack label group.
  38251. * @private
  38252. */
  38253. render(group) {
  38254. const chart = this.axis.chart, options = this.options, formatOption = options.format,
  38255. // Format the text in the label.
  38256. str = formatOption ?
  38257. format(formatOption, this, chart) :
  38258. options.formatter.call(this);
  38259. // Change the text to reflect the new total and set visibility to hidden
  38260. // in case the serie is hidden
  38261. if (this.label) {
  38262. this.label.attr({ text: str, visibility: 'hidden' });
  38263. }
  38264. else {
  38265. // Create new label
  38266. this.label = chart.renderer.label(str, null, void 0, options.shape, void 0, void 0, options.useHTML, false, 'stack-labels');
  38267. const attr = {
  38268. r: options.borderRadius || 0,
  38269. text: str,
  38270. // set default padding to 5 as it is in datalabels #12308
  38271. padding: pick(options.padding, 5),
  38272. visibility: 'hidden' // hidden until setOffset is called
  38273. };
  38274. if (!chart.styledMode) {
  38275. attr.fill = options.backgroundColor;
  38276. attr.stroke = options.borderColor;
  38277. attr['stroke-width'] = options.borderWidth;
  38278. this.label.css(options.style || {});
  38279. }
  38280. this.label.attr(attr);
  38281. if (!this.label.added) {
  38282. this.label.add(group); // add to the labels-group
  38283. }
  38284. }
  38285. // Rank it higher than data labels (#8742)
  38286. this.label.labelrank = chart.plotSizeY;
  38287. fireEvent(this, 'afterRender');
  38288. }
  38289. /**
  38290. * Sets the offset that the stack has from the x value and repositions the
  38291. * label.
  38292. * @private
  38293. */
  38294. setOffset(xOffset, width, boxBottom, boxTop, defaultX, xAxis) {
  38295. const { alignOptions, axis, label, options, textAlign } = this, chart = axis.chart, stackBox = this.getStackBox({
  38296. xOffset,
  38297. width,
  38298. boxBottom,
  38299. boxTop,
  38300. defaultX,
  38301. xAxis
  38302. }), { verticalAlign } = alignOptions;
  38303. if (label && stackBox) {
  38304. const labelBox = label.getBBox(), padding = label.padding;
  38305. let isJustify = pick(options.overflow, 'justify') === 'justify', visible;
  38306. // Reset alignOptions property after justify #12337
  38307. alignOptions.x = options.x || 0;
  38308. alignOptions.y = options.y || 0;
  38309. // Calculate the adjusted Stack position, to take into consideration
  38310. // The size if the labelBox and vertical alignment as
  38311. // well as the text alignment. It's need to be done to work with
  38312. // default SVGLabel.align/justify methods.
  38313. const { x, y } = this.adjustStackPosition({
  38314. labelBox,
  38315. verticalAlign,
  38316. textAlign
  38317. });
  38318. stackBox.x -= x;
  38319. stackBox.y -= y;
  38320. // Align the label to the adjusted box.
  38321. label.align(alignOptions, false, stackBox);
  38322. // Check if label is inside the plotArea #12294
  38323. visible = chart.isInsidePlot(label.alignAttr.x + alignOptions.x + x, label.alignAttr.y + alignOptions.y + y);
  38324. if (!visible) {
  38325. isJustify = false;
  38326. }
  38327. if (isJustify) {
  38328. // Justify stackLabel into the alignBox
  38329. Series.prototype.justifyDataLabel.call(axis, label, alignOptions, label.alignAttr, labelBox, stackBox);
  38330. }
  38331. // Add attr to aviod the default animation of justifyDataLabel.
  38332. // Also add correct rotation with its rotation origin. #15129
  38333. label.attr({
  38334. x: label.alignAttr.x,
  38335. y: label.alignAttr.y,
  38336. rotation: options.rotation,
  38337. rotationOriginX: labelBox.width / 2,
  38338. rotationOriginY: labelBox.height / 2
  38339. });
  38340. // Check if the dataLabel should be visible.
  38341. if (pick(!isJustify && options.crop, true)) {
  38342. visible =
  38343. isNumber(label.x) &&
  38344. isNumber(label.y) &&
  38345. chart.isInsidePlot(label.x - padding + label.width, label.y) &&
  38346. chart.isInsidePlot(label.x + padding, label.y);
  38347. }
  38348. label[visible ? 'show' : 'hide']();
  38349. }
  38350. fireEvent(this, 'afterSetOffset', { xOffset, width });
  38351. }
  38352. /**
  38353. * Adjust the stack BBox position, to take into consideration the alignment
  38354. * of the dataLabel. This is necessary to make the stackDataLabel work with
  38355. * core methods like `SVGLabel.adjust` and `Series.justifyDataLabel`.
  38356. * @param AdjustStackPositionProps
  38357. * @return {{x: number, y: number}} Adjusted BBox position of the stack.
  38358. */
  38359. adjustStackPosition({ labelBox, verticalAlign, textAlign }) {
  38360. const factorMap = {
  38361. bottom: 0,
  38362. middle: 1,
  38363. top: 2,
  38364. right: 1,
  38365. center: 0,
  38366. left: -1
  38367. }, verticalAlignFactor = factorMap[verticalAlign], textAlignFactor = factorMap[textAlign];
  38368. return {
  38369. x: labelBox.width / 2 + (labelBox.width / 2) * textAlignFactor,
  38370. y: (labelBox.height / 2) * verticalAlignFactor
  38371. };
  38372. }
  38373. /**
  38374. * Get the bbox of the stack.
  38375. * @private
  38376. * @function Highcharts.StackItem#getStackBox
  38377. * @return {BBoxObject} The x, y, height, width of the stack.
  38378. */
  38379. getStackBox(stackBoxProps) {
  38380. const stackItem = this, axis = this.axis, chart = axis.chart, { boxTop, defaultX, xOffset, width, boxBottom } = stackBoxProps, totalStackValue = axis.stacking.usePercentage ?
  38381. 100 :
  38382. pick(boxTop, this.total, 0), y = axis.toPixels(totalStackValue), xAxis = stackBoxProps.xAxis || chart.xAxis[0], x = pick(defaultX, xAxis.translate(this.x)) + xOffset, yZero = axis.toPixels(boxBottom ||
  38383. (isNumber(axis.min) &&
  38384. axis.logarithmic &&
  38385. axis.logarithmic.lin2log(axis.min)) ||
  38386. 0), height = Math.abs(y - yZero), inverted = chart.inverted, neg = stackItem.isNegative;
  38387. return inverted ?
  38388. {
  38389. x: (neg ? y : y - height) - chart.plotLeft,
  38390. y: xAxis.height - x - width,
  38391. width: height,
  38392. height: width
  38393. } : {
  38394. x: x + xAxis.transB - chart.plotLeft,
  38395. y: (neg ? y - height : y) - chart.plotTop,
  38396. width: width,
  38397. height: height
  38398. };
  38399. }
  38400. }
  38401. /* *
  38402. *
  38403. * Default Export
  38404. *
  38405. * */
  38406. /* *
  38407. *
  38408. * API Declarations
  38409. *
  38410. * */
  38411. /**
  38412. * Stack of data points
  38413. *
  38414. * @product highcharts
  38415. *
  38416. * @interface Highcharts.StackItemObject
  38417. */ /**
  38418. * Alignment settings
  38419. * @name Highcharts.StackItemObject#alignOptions
  38420. * @type {Highcharts.AlignObject}
  38421. */ /**
  38422. * Related axis
  38423. * @name Highcharts.StackItemObject#axis
  38424. * @type {Highcharts.Axis}
  38425. */ /**
  38426. * Cumulative value of the stacked data points
  38427. * @name Highcharts.StackItemObject#cumulative
  38428. * @type {number}
  38429. */ /**
  38430. * True if on the negative side
  38431. * @name Highcharts.StackItemObject#isNegative
  38432. * @type {boolean}
  38433. */ /**
  38434. * Related SVG element
  38435. * @name Highcharts.StackItemObject#label
  38436. * @type {Highcharts.SVGElement}
  38437. */ /**
  38438. * Related stack options
  38439. * @name Highcharts.StackItemObject#options
  38440. * @type {Highcharts.YAxisStackLabelsOptions}
  38441. */ /**
  38442. * Total value of the stacked data points
  38443. * @name Highcharts.StackItemObject#total
  38444. * @type {number}
  38445. */ /**
  38446. * Shared x value of the stack
  38447. * @name Highcharts.StackItemObject#x
  38448. * @type {number}
  38449. */
  38450. ''; // keeps doclets above in JS file
  38451. return StackItem;
  38452. });
  38453. _registerModule(_modules, 'Core/Axis/Stacking/StackingAxis.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Utilities.js']], function (A, Axis, SeriesRegistry, StackItem, U) {
  38454. /* *
  38455. *
  38456. * (c) 2010-2021 Torstein Honsi
  38457. *
  38458. * License: www.highcharts.com/license
  38459. *
  38460. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38461. *
  38462. * */
  38463. const { getDeferredAnimation } = A;
  38464. const { series: { prototype: seriesProto } } = SeriesRegistry;
  38465. const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, isArray, isNumber, objectEach, pick } = U;
  38466. /* *
  38467. *
  38468. * Functions
  38469. *
  38470. * */
  38471. /**
  38472. * Generate stacks for each series and calculate stacks total values
  38473. *
  38474. * @private
  38475. * @function Highcharts.Chart#getStacks
  38476. */
  38477. function chartGetStacks() {
  38478. const chart = this, inverted = chart.inverted;
  38479. // reset stacks for each yAxis
  38480. chart.yAxis.forEach((axis) => {
  38481. if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
  38482. axis.stacking.oldStacks = axis.stacking.stacks;
  38483. }
  38484. });
  38485. chart.series.forEach((series) => {
  38486. const xAxisOptions = series.xAxis && series.xAxis.options || {};
  38487. if (series.options.stacking &&
  38488. (series.visible === true ||
  38489. chart.options.chart.ignoreHiddenSeries === false)) {
  38490. series.stackKey = [
  38491. series.type,
  38492. pick(series.options.stack, ''),
  38493. inverted ? xAxisOptions.top : xAxisOptions.left,
  38494. inverted ? xAxisOptions.height : xAxisOptions.width
  38495. ].join(',');
  38496. }
  38497. });
  38498. }
  38499. /**
  38500. * @private
  38501. */
  38502. function onAxisDestroy() {
  38503. const stacking = this.stacking;
  38504. if (!stacking) {
  38505. return;
  38506. }
  38507. const stacks = stacking.stacks;
  38508. // Destroy each stack total
  38509. objectEach(stacks, function (stack, stackKey) {
  38510. destroyObjectProperties(stack);
  38511. stacks[stackKey] = null;
  38512. });
  38513. if (stacking &&
  38514. stacking.stackTotalGroup) {
  38515. stacking.stackTotalGroup.destroy();
  38516. }
  38517. }
  38518. /**
  38519. * @private
  38520. */
  38521. function onAxisInit() {
  38522. if (this.coll === 'yAxis' && !this.stacking) {
  38523. this.stacking = new AxisAdditions(this);
  38524. }
  38525. }
  38526. /**
  38527. * Get stack indicator, according to it's x-value, to determine points with the
  38528. * same x-value
  38529. *
  38530. * @private
  38531. * @function Highcharts.Series#getStackIndicator
  38532. */
  38533. function seriesGetStackIndicator(stackIndicator, x, index, key) {
  38534. // Update stack indicator, when:
  38535. // first point in a stack || x changed || stack type (negative vs positive)
  38536. // changed:
  38537. if (!defined(stackIndicator) ||
  38538. stackIndicator.x !== x ||
  38539. (key && stackIndicator.stackKey !== key)) {
  38540. stackIndicator = {
  38541. x: x,
  38542. index: 0,
  38543. key: key,
  38544. stackKey: key
  38545. };
  38546. }
  38547. else {
  38548. (stackIndicator).index++;
  38549. }
  38550. stackIndicator.key =
  38551. [index, x, stackIndicator.index].join(',');
  38552. return stackIndicator;
  38553. }
  38554. /**
  38555. * Iterate over all stacks and compute the absolute values to percent
  38556. *
  38557. * @private
  38558. * @function Highcharts.Series#modifyStacks
  38559. */
  38560. function seriesModifyStacks() {
  38561. const series = this, yAxis = series.yAxis, stackKey = series.stackKey, stacks = yAxis.stacking.stacks, processedXData = series.processedXData, stacking = series.options.stacking, stacker = series[stacking + 'Stacker'];
  38562. let stackIndicator;
  38563. if (stacker) { // Modifier function exists (Series.percentStacker etc.)
  38564. [stackKey, '-' + stackKey].forEach((key) => {
  38565. let i = processedXData.length, x, stack, pointExtremes;
  38566. while (i--) {
  38567. x = processedXData[i];
  38568. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  38569. stack = stacks[key] && stacks[key][x];
  38570. pointExtremes =
  38571. stack && stack.points[stackIndicator.key];
  38572. if (pointExtremes) {
  38573. stacker.call(series, pointExtremes, stack, i);
  38574. }
  38575. }
  38576. });
  38577. }
  38578. }
  38579. /**
  38580. * Modifier function for percent stacks. Blows up the stack to 100%.
  38581. *
  38582. * @private
  38583. * @function Highcharts.Series#percentStacker
  38584. */
  38585. function seriesPercentStacker(pointExtremes, stack, i) {
  38586. const totalFactor = stack.total ? 100 / stack.total : 0;
  38587. // Y bottom value
  38588. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  38589. // Y value
  38590. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  38591. this.stackedYData[i] = pointExtremes[1];
  38592. }
  38593. /**
  38594. * Set grouped points in a stack-like object. When `centerInCategory` is true,
  38595. * and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
  38596. * to handle grouping of points within the same category.
  38597. *
  38598. * @private
  38599. * @function Highcharts.Series#setStackedPoints
  38600. * @return {void}
  38601. */
  38602. function seriesSetGroupedPoints() {
  38603. const stacking = this.yAxis.stacking;
  38604. if (this.options.centerInCategory &&
  38605. (this.is('column') || this.is('columnrange')) &&
  38606. // With stacking enabled, we already have stacks that we can compute
  38607. // from
  38608. !this.options.stacking &&
  38609. // With only one series, we don't need to consider centerInCategory
  38610. this.chart.series.length > 1) {
  38611. seriesProto.setStackedPoints.call(this, 'group');
  38612. // After updating, if we now have proper stacks, we must delete the group
  38613. // pseudo stacks (#14986)
  38614. }
  38615. else if (stacking) {
  38616. objectEach(stacking.stacks, (type, key) => {
  38617. if (key.slice(-5) === 'group') {
  38618. objectEach(type, (stack) => stack.destroy());
  38619. delete stacking.stacks[key];
  38620. }
  38621. });
  38622. }
  38623. }
  38624. /**
  38625. * Adds series' points value to corresponding stack
  38626. *
  38627. * @private
  38628. * @function Highcharts.Series#setStackedPoints
  38629. */
  38630. function seriesSetStackedPoints(stackingParam) {
  38631. const chart = this.chart, stacking = stackingParam || this.options.stacking;
  38632. if (!stacking || (this.visible !== true &&
  38633. chart.options.chart.ignoreHiddenSeries !== false)) {
  38634. return;
  38635. }
  38636. const series = this, xData = series.processedXData, yData = series.processedYData, stackedYData = [], yDataLength = yData.length, seriesOptions = series.options, threshold = seriesOptions.threshold, stackThreshold = pick(seriesOptions.startFromThreshold && threshold, 0), stackOption = seriesOptions.stack, stackKey = stackingParam ? `${series.type},${stacking}` : series.stackKey, negKey = '-' + stackKey, negStacks = series.negStacks, yAxis = stacking === 'group' ?
  38637. chart.yAxis[0] :
  38638. series.yAxis, stacks = yAxis.stacking.stacks, oldStacks = yAxis.stacking.oldStacks;
  38639. let stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  38640. yAxis.stacking.stacksTouched += 1;
  38641. // loop over the non-null y values and read them into a local array
  38642. for (i = 0; i < yDataLength; i++) {
  38643. x = xData[i];
  38644. y = yData[i];
  38645. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  38646. pointKey = stackIndicator.key;
  38647. // Read stacked values into a stack based on the x value,
  38648. // the sign of y and the stack key. Stacking is also handled for null
  38649. // values (#739)
  38650. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  38651. key = isNegative ? negKey : stackKey;
  38652. // Create empty object for this stack if it doesn't exist yet
  38653. if (!stacks[key]) {
  38654. stacks[key] = {};
  38655. }
  38656. // Initialize StackItem for this x
  38657. if (!stacks[key][x]) {
  38658. if (oldStacks[key] &&
  38659. oldStacks[key][x]) {
  38660. stacks[key][x] = oldStacks[key][x];
  38661. stacks[key][x].total = null;
  38662. }
  38663. else {
  38664. stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, !!isNegative, x, stackOption);
  38665. }
  38666. }
  38667. // If the StackItem doesn't exist, create it first
  38668. stack = stacks[key][x];
  38669. if (y !== null) {
  38670. stack.points[pointKey] = stack.points[series.index] =
  38671. [pick(stack.cumulative, stackThreshold)];
  38672. // Record the base of the stack
  38673. if (!defined(stack.cumulative)) {
  38674. stack.base = pointKey;
  38675. }
  38676. stack.touched = yAxis.stacking.stacksTouched;
  38677. // In area charts, if there are multiple points on the same X value,
  38678. // let the area fill the full span of those points
  38679. if (stackIndicator.index > 0 && series.singleStacks === false) {
  38680. stack.points[pointKey][0] =
  38681. stack.points[series.index + ',' + x + ',0'][0];
  38682. }
  38683. // When updating to null, reset the point stack (#7493)
  38684. }
  38685. else {
  38686. stack.points[pointKey] = stack.points[series.index] =
  38687. null;
  38688. }
  38689. // Add value to the stack total
  38690. if (stacking === 'percent') {
  38691. // Percent stacked column, totals are the same for the positive and
  38692. // negative stacks
  38693. other = isNegative ? stackKey : negKey;
  38694. if (negStacks && stacks[other] && stacks[other][x]) {
  38695. other = stacks[other][x];
  38696. stack.total = other.total =
  38697. Math.max(other.total, stack.total) +
  38698. Math.abs(y) ||
  38699. 0;
  38700. // Percent stacked areas
  38701. }
  38702. else {
  38703. stack.total =
  38704. correctFloat(stack.total + (Math.abs(y) || 0));
  38705. }
  38706. }
  38707. else if (stacking === 'group') {
  38708. if (isArray(y)) {
  38709. y = y[0];
  38710. }
  38711. // In this stack, the total is the number of valid points
  38712. if (y !== null) {
  38713. stack.total = (stack.total || 0) + 1;
  38714. }
  38715. }
  38716. else {
  38717. stack.total = correctFloat(stack.total + (y || 0));
  38718. }
  38719. if (stacking === 'group') {
  38720. // This point's index within the stack, pushed to stack.points[1]
  38721. stack.cumulative = (stack.total || 1) - 1;
  38722. }
  38723. else {
  38724. stack.cumulative = correctFloat(pick(stack.cumulative, stackThreshold) +
  38725. (y || 0));
  38726. }
  38727. if (y !== null) {
  38728. stack.points[pointKey].push(stack.cumulative);
  38729. stackedYData[i] = stack.cumulative;
  38730. stack.hasValidPoints = true;
  38731. }
  38732. }
  38733. if (stacking === 'percent') {
  38734. yAxis.stacking.usePercentage = true;
  38735. }
  38736. if (stacking !== 'group') {
  38737. this.stackedYData = stackedYData; // To be used in getExtremes
  38738. }
  38739. // Reset old stacks
  38740. yAxis.stacking.oldStacks = {};
  38741. }
  38742. /* *
  38743. *
  38744. * Classes
  38745. *
  38746. * */
  38747. /**
  38748. * Adds stacking support to axes.
  38749. * @private
  38750. * @class
  38751. */
  38752. class AxisAdditions {
  38753. /* *
  38754. *
  38755. * Constructors
  38756. *
  38757. * */
  38758. constructor(axis) {
  38759. this.oldStacks = {};
  38760. this.stacks = {};
  38761. this.stacksTouched = 0;
  38762. this.axis = axis;
  38763. }
  38764. /* *
  38765. *
  38766. * Functions
  38767. *
  38768. * */
  38769. /**
  38770. * Build the stacks from top down
  38771. * @private
  38772. */
  38773. buildStacks() {
  38774. const stacking = this;
  38775. const axis = stacking.axis;
  38776. const axisSeries = axis.series;
  38777. const reversedStacks = axis.options.reversedStacks;
  38778. const len = axisSeries.length;
  38779. let actualSeries, i;
  38780. stacking.usePercentage = false;
  38781. i = len;
  38782. while (i--) {
  38783. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  38784. actualSeries.setStackedPoints();
  38785. actualSeries.setGroupedPoints();
  38786. }
  38787. // Loop up again to compute percent and stream stack
  38788. for (i = 0; i < len; i++) {
  38789. axisSeries[i].modifyStacks();
  38790. }
  38791. fireEvent(axis, 'afterBuildStacks');
  38792. }
  38793. /**
  38794. * @private
  38795. */
  38796. cleanStacks() {
  38797. const stacking = this;
  38798. let stacks;
  38799. if (stacking.oldStacks) {
  38800. stacks = stacking.stacks = stacking.oldStacks;
  38801. }
  38802. // reset stacks
  38803. objectEach(stacks, function (type) {
  38804. objectEach(type, function (stack) {
  38805. stack.cumulative = stack.total;
  38806. });
  38807. });
  38808. }
  38809. /**
  38810. * Set all the stacks to initial states and destroy unused ones.
  38811. * @private
  38812. */
  38813. resetStacks() {
  38814. objectEach(this.stacks, (type) => {
  38815. objectEach(type, (stack, x) => {
  38816. // Clean up memory after point deletion (#1044, #4320)
  38817. if (isNumber(stack.touched) &&
  38818. stack.touched < this.stacksTouched) {
  38819. stack.destroy();
  38820. delete type[x];
  38821. // Reset stacks
  38822. }
  38823. else {
  38824. stack.total = null;
  38825. stack.cumulative = null;
  38826. }
  38827. });
  38828. });
  38829. }
  38830. /**
  38831. * @private
  38832. */
  38833. renderStackTotals() {
  38834. const stacking = this, axis = stacking.axis, chart = axis.chart, renderer = chart.renderer, stacks = stacking.stacks, stackLabelsAnim = axis.options.stackLabels &&
  38835. axis.options.stackLabels.animation, animationConfig = getDeferredAnimation(chart, stackLabelsAnim || false), stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
  38836. renderer
  38837. .g('stack-labels')
  38838. .attr({
  38839. zIndex: 6,
  38840. opacity: 0
  38841. })
  38842. .add());
  38843. // plotLeft/Top will change when y axis gets wider so we need to
  38844. // translate the stackTotalGroup at every render call. See bug #506
  38845. // and #516
  38846. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  38847. // Render each stack total
  38848. objectEach(stacks, function (type) {
  38849. objectEach(type, function (stack) {
  38850. stack.render(stackTotalGroup);
  38851. });
  38852. });
  38853. stackTotalGroup.animate({
  38854. opacity: 1
  38855. }, animationConfig);
  38856. }
  38857. }
  38858. /* *
  38859. *
  38860. * Composition
  38861. *
  38862. * */
  38863. var StackingAxis;
  38864. (function (StackingAxis) {
  38865. /* *
  38866. *
  38867. * Constants
  38868. *
  38869. * */
  38870. const composedMembers = [];
  38871. /* *
  38872. *
  38873. * Functions
  38874. *
  38875. * */
  38876. /**
  38877. * Extends axis with stacking support.
  38878. * @private
  38879. */
  38880. function compose(AxisClass, ChartClass, SeriesClass) {
  38881. if (U.pushUnique(composedMembers, AxisClass)) {
  38882. addEvent(AxisClass, 'init', onAxisInit);
  38883. addEvent(AxisClass, 'destroy', onAxisDestroy);
  38884. }
  38885. if (U.pushUnique(composedMembers, ChartClass)) {
  38886. const chartProto = ChartClass.prototype;
  38887. chartProto.getStacks = chartGetStacks;
  38888. }
  38889. if (U.pushUnique(composedMembers, SeriesClass)) {
  38890. const seriesProto = SeriesClass.prototype;
  38891. seriesProto.getStackIndicator = seriesGetStackIndicator;
  38892. seriesProto.modifyStacks = seriesModifyStacks;
  38893. seriesProto.percentStacker = seriesPercentStacker;
  38894. seriesProto.setGroupedPoints = seriesSetGroupedPoints;
  38895. seriesProto.setStackedPoints = seriesSetStackedPoints;
  38896. }
  38897. }
  38898. StackingAxis.compose = compose;
  38899. })(StackingAxis || (StackingAxis = {}));
  38900. /* *
  38901. *
  38902. * Default Export
  38903. *
  38904. * */
  38905. return StackingAxis;
  38906. });
  38907. _registerModule(_modules, 'Series/Line/LineSeries.js', [_modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Series, SeriesRegistry, U) {
  38908. /* *
  38909. *
  38910. * (c) 2010-2021 Torstein Honsi
  38911. *
  38912. * License: www.highcharts.com/license
  38913. *
  38914. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38915. *
  38916. * */
  38917. const { defined, merge } = U;
  38918. /* *
  38919. *
  38920. * Class
  38921. *
  38922. * */
  38923. /**
  38924. * The line series is the base type and is therefor the series base prototype.
  38925. *
  38926. * @private
  38927. */
  38928. class LineSeries extends Series {
  38929. constructor() {
  38930. /* *
  38931. *
  38932. * Static Functions
  38933. *
  38934. * */
  38935. super(...arguments);
  38936. /* *
  38937. *
  38938. * Properties
  38939. *
  38940. * */
  38941. this.data = void 0;
  38942. this.options = void 0;
  38943. this.points = void 0;
  38944. }
  38945. /* *
  38946. *
  38947. * Functions
  38948. *
  38949. * */
  38950. /**
  38951. * Draw the graph. Called internally when rendering line-like series
  38952. * types. The first time it generates the `series.graph` item and
  38953. * optionally other series-wide items like `series.area` for area
  38954. * charts. On subsequent calls these items are updated with new
  38955. * positions and attributes.
  38956. *
  38957. * @function Highcharts.Series#drawGraph
  38958. */
  38959. drawGraph() {
  38960. const series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode;
  38961. let props = [[
  38962. 'graph',
  38963. 'highcharts-graph'
  38964. ]];
  38965. // Presentational properties
  38966. if (!styledMode) {
  38967. props[0].push((options.lineColor ||
  38968. this.color ||
  38969. "#cccccc" /* Palette.neutralColor20 */ // when colorByPoint = true
  38970. ), options.dashStyle);
  38971. }
  38972. props = series.getZonesGraphs(props);
  38973. // Draw the graph
  38974. props.forEach(function (prop, i) {
  38975. const graphKey = prop[0];
  38976. let attribs, graph = series[graphKey];
  38977. const verb = graph ? 'animate' : 'attr';
  38978. if (graph) {
  38979. graph.endX = series.preventGraphAnimation ?
  38980. null :
  38981. graphPath.xMap;
  38982. graph.animate({ d: graphPath });
  38983. }
  38984. else if (graphPath.length) { // #1487
  38985. /**
  38986. * SVG element of area-based charts. Can be used for styling
  38987. * purposes. If zones are configured, this element will be
  38988. * hidden and replaced by multiple zone areas, accessible
  38989. * via `series['zone-area-x']` (where x is a number,
  38990. * starting with 0).
  38991. *
  38992. * @name Highcharts.Series#area
  38993. * @type {Highcharts.SVGElement|undefined}
  38994. */
  38995. /**
  38996. * SVG element of line-based charts. Can be used for styling
  38997. * purposes. If zones are configured, this element will be
  38998. * hidden and replaced by multiple zone lines, accessible
  38999. * via `series['zone-graph-x']` (where x is a number,
  39000. * starting with 0).
  39001. *
  39002. * @name Highcharts.Series#graph
  39003. * @type {Highcharts.SVGElement|undefined}
  39004. */
  39005. series[graphKey] = graph = series.chart.renderer
  39006. .path(graphPath)
  39007. .addClass(prop[1])
  39008. .attr({ zIndex: 1 }) // #1069
  39009. .add(series.group);
  39010. }
  39011. if (graph && !styledMode) {
  39012. attribs = {
  39013. 'stroke': prop[2],
  39014. 'stroke-width': options.lineWidth || 0,
  39015. // Polygon series use filled graph
  39016. 'fill': (series.fillGraph && series.color) || 'none'
  39017. };
  39018. // Apply dash style
  39019. if (prop[3]) {
  39020. attribs.dashstyle = prop[3];
  39021. // The reason for the `else if` is that linecaps don't mix well
  39022. // with dashstyle. The gaps get partially filled by the
  39023. // linecap.
  39024. }
  39025. else if (options.linecap !== 'square') {
  39026. attribs['stroke-linecap'] =
  39027. attribs['stroke-linejoin'] = 'round';
  39028. }
  39029. graph[verb](attribs)
  39030. // Add shadow to normal series (0) or to first
  39031. // zone (1) #3932
  39032. .shadow((i < 2) && options.shadow);
  39033. }
  39034. // Helpers for animation
  39035. if (graph) {
  39036. graph.startX = graphPath.xMap;
  39037. graph.isArea = graphPath.isArea; // For arearange animation
  39038. }
  39039. });
  39040. }
  39041. // eslint-disable-next-line valid-jsdoc
  39042. /**
  39043. * Get the graph path.
  39044. *
  39045. * @private
  39046. */
  39047. getGraphPath(points, nullsAsZeroes, connectCliffs) {
  39048. const series = this, options = series.options, graphPath = [], xMap = [];
  39049. let gap, step = options.step;
  39050. points = points || series.points;
  39051. // Bottom of a stack is reversed
  39052. const reversed = points.reversed;
  39053. if (reversed) {
  39054. points.reverse();
  39055. }
  39056. // Reverse the steps (#5004)
  39057. step = {
  39058. right: 1,
  39059. center: 2
  39060. }[step] || (step && 3);
  39061. if (step && reversed) {
  39062. step = 4 - step;
  39063. }
  39064. // Remove invalid points, especially in spline (#5015)
  39065. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  39066. // Build the line
  39067. points.forEach(function (point, i) {
  39068. const plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1], isNull = point.isNull || typeof plotY !== 'number';
  39069. // the path to this point from the previous
  39070. let pathToPoint;
  39071. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  39072. !connectCliffs) {
  39073. gap = true; // ... and continue
  39074. }
  39075. // Line series, nullsAsZeroes is not handled
  39076. if (isNull && !defined(nullsAsZeroes) && i > 0) {
  39077. gap = !options.connectNulls;
  39078. // Area series, nullsAsZeroes is set
  39079. }
  39080. else if (isNull && !nullsAsZeroes) {
  39081. gap = true;
  39082. }
  39083. else {
  39084. if (i === 0 || gap) {
  39085. pathToPoint = [[
  39086. 'M',
  39087. point.plotX,
  39088. point.plotY
  39089. ]];
  39090. // Generate the spline as defined in the SplineSeries object
  39091. }
  39092. else if (series.getPointSpline) {
  39093. pathToPoint = [series.getPointSpline(points, point, i)];
  39094. }
  39095. else if (step) {
  39096. if (step === 1) { // right
  39097. pathToPoint = [[
  39098. 'L',
  39099. lastPoint.plotX,
  39100. plotY
  39101. ]];
  39102. }
  39103. else if (step === 2) { // center
  39104. pathToPoint = [[
  39105. 'L',
  39106. (lastPoint.plotX + plotX) / 2,
  39107. lastPoint.plotY
  39108. ], [
  39109. 'L',
  39110. (lastPoint.plotX + plotX) / 2,
  39111. plotY
  39112. ]];
  39113. }
  39114. else {
  39115. pathToPoint = [[
  39116. 'L',
  39117. plotX,
  39118. lastPoint.plotY
  39119. ]];
  39120. }
  39121. pathToPoint.push([
  39122. 'L',
  39123. plotX,
  39124. plotY
  39125. ]);
  39126. }
  39127. else {
  39128. // normal line to next point
  39129. pathToPoint = [[
  39130. 'L',
  39131. plotX,
  39132. plotY
  39133. ]];
  39134. }
  39135. // Prepare for animation. When step is enabled, there are
  39136. // two path nodes for each x value.
  39137. xMap.push(point.x);
  39138. if (step) {
  39139. xMap.push(point.x);
  39140. if (step === 2) { // step = center (#8073)
  39141. xMap.push(point.x);
  39142. }
  39143. }
  39144. graphPath.push.apply(graphPath, pathToPoint);
  39145. gap = false;
  39146. }
  39147. });
  39148. graphPath.xMap = xMap;
  39149. series.graphPath = graphPath;
  39150. return graphPath;
  39151. }
  39152. // eslint-disable-next-line valid-jsdoc
  39153. /**
  39154. * Get zones properties for building graphs. Extendable by series with
  39155. * multiple lines within one series.
  39156. *
  39157. * @private
  39158. */
  39159. getZonesGraphs(props) {
  39160. // Add the zone properties if any
  39161. this.zones.forEach(function (zone, i) {
  39162. const propset = [
  39163. 'zone-graph-' + i,
  39164. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  39165. (zone.className || '')
  39166. ];
  39167. if (!this.chart.styledMode) {
  39168. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  39169. }
  39170. props.push(propset);
  39171. }, this);
  39172. return props;
  39173. }
  39174. }
  39175. LineSeries.defaultOptions = merge(Series.defaultOptions,
  39176. /**
  39177. * General options for all series types.
  39178. *
  39179. * @optionparent plotOptions.series
  39180. */
  39181. {
  39182. legendSymbol: 'lineMarker'
  39183. });
  39184. SeriesRegistry.registerSeriesType('line', LineSeries);
  39185. /* *
  39186. *
  39187. * Default Export
  39188. *
  39189. * */
  39190. /* *
  39191. *
  39192. * API Options
  39193. *
  39194. * */
  39195. /**
  39196. * A line series displays information as a series of data points connected by
  39197. * straight line segments.
  39198. *
  39199. * @sample {highcharts} highcharts/demo/line-basic/
  39200. * Line chart
  39201. * @sample {highstock} stock/demo/basic-line/
  39202. * Line chart
  39203. *
  39204. * @extends plotOptions.series
  39205. * @product highcharts highstock
  39206. * @apioption plotOptions.line
  39207. */
  39208. /**
  39209. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  39210. * of a line graph. Round means that lines are rounded in the ends and
  39211. * bends.
  39212. *
  39213. * @type {Highcharts.SeriesLinecapValue}
  39214. * @default round
  39215. * @since 3.0.7
  39216. * @apioption plotOptions.line.linecap
  39217. */
  39218. /**
  39219. * A `line` series. If the [type](#series.line.type) option is not
  39220. * specified, it is inherited from [chart.type](#chart.type).
  39221. *
  39222. * @extends series,plotOptions.line
  39223. * @excluding dataParser,dataURL
  39224. * @product highcharts highstock
  39225. * @apioption series.line
  39226. */
  39227. /**
  39228. * An array of data points for the series. For the `line` series type,
  39229. * points can be given in the following ways:
  39230. *
  39231. * 1. An array of numerical values. In this case, the numerical values will be
  39232. * interpreted as `y` options. The `x` values will be automatically
  39233. * calculated, either starting at 0 and incremented by 1, or from
  39234. * `pointStart` and `pointInterval` given in the series options. If the axis
  39235. * has categories, these will be used. Example:
  39236. * ```js
  39237. * data: [0, 5, 3, 5]
  39238. * ```
  39239. *
  39240. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39241. * `x,y`. If the first value is a string, it is applied as the name of the
  39242. * point, and the `x` value is inferred.
  39243. * ```js
  39244. * data: [
  39245. * [0, 1],
  39246. * [1, 2],
  39247. * [2, 8]
  39248. * ]
  39249. * ```
  39250. *
  39251. * 3. An array of objects with named values. The following snippet shows only a
  39252. * few settings, see the complete options set below. If the total number of
  39253. * data points exceeds the series'
  39254. * [turboThreshold](#series.line.turboThreshold),
  39255. * this option is not available.
  39256. * ```js
  39257. * data: [{
  39258. * x: 1,
  39259. * y: 9,
  39260. * name: "Point2",
  39261. * color: "#00FF00"
  39262. * }, {
  39263. * x: 1,
  39264. * y: 6,
  39265. * name: "Point1",
  39266. * color: "#FF00FF"
  39267. * }]
  39268. * ```
  39269. *
  39270. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  39271. * additional declaration to allow custom data types:
  39272. * ```ts
  39273. * declare module `highcharts` {
  39274. * interface PointOptionsObject {
  39275. * custom: Record<string, (boolean|number|string)>;
  39276. * }
  39277. * }
  39278. * ```
  39279. *
  39280. * @sample {highcharts} highcharts/chart/reflow-true/
  39281. * Numerical values
  39282. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39283. * Arrays of numeric x and y
  39284. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39285. * Arrays of datetime x and y
  39286. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39287. * Arrays of point.name and y
  39288. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39289. * Config objects
  39290. *
  39291. * @declare Highcharts.PointOptionsObject
  39292. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39293. * @apioption series.line.data
  39294. */
  39295. /**
  39296. * An additional, individual class name for the data point's graphic
  39297. * representation. Changes to a point's color will also be reflected in a
  39298. * chart's legend and tooltip.
  39299. *
  39300. * @sample {highcharts} highcharts/css/point-series-classname
  39301. *
  39302. * @type {string}
  39303. * @since 5.0.0
  39304. * @product highcharts gantt
  39305. * @apioption series.line.data.className
  39306. */
  39307. /**
  39308. * Individual color for the point. By default the color is pulled from
  39309. * the global `colors` array.
  39310. *
  39311. * In styled mode, the `color` option doesn't take effect. Instead, use
  39312. * `colorIndex`.
  39313. *
  39314. * @sample {highcharts} highcharts/point/color/
  39315. * Mark the highest point
  39316. *
  39317. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39318. * @product highcharts highstock gantt
  39319. * @apioption series.line.data.color
  39320. */
  39321. /**
  39322. * A specific color index to use for the point, so its graphic representations
  39323. * are given the class name `highcharts-color-{n}`. In styled mode this will
  39324. * change the color of the graphic. In non-styled mode, the color is set by the
  39325. * `fill` attribute, so the change in class name won't have a visual effect by
  39326. * default.
  39327. *
  39328. * Since v11, CSS variables on the form `--highcharts-color-{n}` make changing
  39329. * the color scheme very convenient.
  39330. *
  39331. * @sample {highcharts} highcharts/css/colorindex/
  39332. * Series and point color index
  39333. *
  39334. * @type {number}
  39335. * @since 5.0.0
  39336. * @product highcharts gantt
  39337. * @apioption series.line.data.colorIndex
  39338. */
  39339. /**
  39340. * A reserved subspace to store options and values for customized functionality.
  39341. * Here you can add additional data for your own event callbacks and formatter
  39342. * callbacks.
  39343. *
  39344. * @sample {highcharts} highcharts/point/custom/
  39345. * Point and series with custom data
  39346. *
  39347. * @type {Highcharts.Dictionary<*>}
  39348. * @apioption series.line.data.custom
  39349. */
  39350. /**
  39351. * Individual data label for each point. The options are the same as
  39352. * the ones for [plotOptions.series.dataLabels](
  39353. * #plotOptions.series.dataLabels).
  39354. *
  39355. * @sample highcharts/point/datalabels/
  39356. * Show a label for the last value
  39357. *
  39358. * @declare Highcharts.DataLabelsOptions
  39359. * @extends plotOptions.line.dataLabels
  39360. * @product highcharts highstock gantt
  39361. * @apioption series.line.data.dataLabels
  39362. */
  39363. /**
  39364. * A description of the point to add to the screen reader information
  39365. * about the point.
  39366. *
  39367. * @type {string}
  39368. * @since 5.0.0
  39369. * @requires modules/accessibility
  39370. * @apioption series.line.data.description
  39371. */
  39372. /**
  39373. * An id for the point. This can be used after render time to get a
  39374. * pointer to the point object through `chart.get()`.
  39375. *
  39376. * @sample {highcharts} highcharts/point/id/
  39377. * Remove an id'd point
  39378. *
  39379. * @type {string}
  39380. * @since 1.2.0
  39381. * @product highcharts highstock gantt
  39382. * @apioption series.line.data.id
  39383. */
  39384. /**
  39385. * The rank for this point's data label in case of collision. If two
  39386. * data labels are about to overlap, only the one with the highest `labelrank`
  39387. * will be drawn.
  39388. *
  39389. * @type {number}
  39390. * @apioption series.line.data.labelrank
  39391. */
  39392. /**
  39393. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  39394. *
  39395. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  39396. *
  39397. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39398. * Point names
  39399. *
  39400. * @type {string}
  39401. * @apioption series.line.data.name
  39402. */
  39403. /**
  39404. * Whether the data point is selected initially.
  39405. *
  39406. * @type {boolean}
  39407. * @default false
  39408. * @product highcharts highstock gantt
  39409. * @apioption series.line.data.selected
  39410. */
  39411. /**
  39412. * The x value of the point. For datetime axes, the X value is the timestamp
  39413. * in milliseconds since 1970.
  39414. *
  39415. * @type {number}
  39416. * @product highcharts highstock
  39417. * @apioption series.line.data.x
  39418. */
  39419. /**
  39420. * The y value of the point.
  39421. *
  39422. * @type {number|null}
  39423. * @product highcharts highstock
  39424. * @apioption series.line.data.y
  39425. */
  39426. /**
  39427. * The individual point events.
  39428. *
  39429. * @extends plotOptions.series.point.events
  39430. * @product highcharts highstock gantt
  39431. * @apioption series.line.data.events
  39432. */
  39433. /**
  39434. * Options for the point markers of line-like series.
  39435. *
  39436. * @declare Highcharts.PointMarkerOptionsObject
  39437. * @extends plotOptions.series.marker
  39438. * @product highcharts highstock
  39439. * @apioption series.line.data.marker
  39440. */
  39441. ''; // include precedent doclets in transpilat
  39442. return LineSeries;
  39443. });
  39444. _registerModule(_modules, 'Series/Area/AreaSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Color, SeriesRegistry, U) {
  39445. /* *
  39446. *
  39447. * (c) 2010-2021 Torstein Honsi
  39448. *
  39449. * License: www.highcharts.com/license
  39450. *
  39451. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39452. *
  39453. * */
  39454. const { parse: color } = Color;
  39455. const { seriesTypes: { line: LineSeries } } = SeriesRegistry;
  39456. const { extend, merge, objectEach, pick } = U;
  39457. /* *
  39458. *
  39459. * Class
  39460. *
  39461. * */
  39462. /**
  39463. * Area series type.
  39464. *
  39465. * @private
  39466. * @class
  39467. * @name AreaSeries
  39468. *
  39469. * @augments LineSeries
  39470. */
  39471. class AreaSeries extends LineSeries {
  39472. constructor() {
  39473. /* *
  39474. *
  39475. * Static Properties
  39476. *
  39477. * */
  39478. super(...arguments);
  39479. this.data = void 0;
  39480. this.options = void 0;
  39481. this.points = void 0;
  39482. /* eslint-enable valid-jsdoc */
  39483. }
  39484. /* *
  39485. *
  39486. * Functions
  39487. *
  39488. * */
  39489. /* eslint-disable valid-jsdoc */
  39490. /**
  39491. * Draw the graph and the underlying area. This method calls the Series
  39492. * base function and adds the area. The areaPath is calculated in the
  39493. * getSegmentPath method called from Series.prototype.drawGraph.
  39494. * @private
  39495. */
  39496. drawGraph() {
  39497. // Define or reset areaPath
  39498. this.areaPath = [];
  39499. // Call the base method
  39500. super.drawGraph.apply(this);
  39501. // Define local variables
  39502. const series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  39503. 'area',
  39504. 'highcharts-area',
  39505. this.color,
  39506. options.fillColor
  39507. ]]; // area name, main color, fill color
  39508. zones.forEach(function (zone, i) {
  39509. props.push([
  39510. 'zone-area-' + i,
  39511. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  39512. zone.className,
  39513. zone.color || series.color,
  39514. zone.fillColor || options.fillColor
  39515. ]);
  39516. });
  39517. props.forEach(function (prop) {
  39518. const areaKey = prop[0], attribs = {};
  39519. let area = series[areaKey];
  39520. const verb = area ? 'animate' : 'attr';
  39521. // Create or update the area
  39522. if (area) { // update
  39523. area.endX = series.preventGraphAnimation ?
  39524. null :
  39525. areaPath.xMap;
  39526. area.animate({ d: areaPath });
  39527. }
  39528. else { // create
  39529. attribs.zIndex = 0; // #1069
  39530. area = series[areaKey] = series.chart.renderer
  39531. .path(areaPath)
  39532. .addClass(prop[1])
  39533. .add(series.group);
  39534. area.isArea = true;
  39535. }
  39536. if (!series.chart.styledMode) {
  39537. // If there is fillColor defined for the area, set it
  39538. if (prop[3]) {
  39539. attribs.fill = prop[3];
  39540. }
  39541. else {
  39542. // Otherwise, we set it to the series color and add
  39543. // fill-opacity (#18939)
  39544. attribs.fill = prop[2];
  39545. attribs['fill-opacity'] = pick(options.fillOpacity, 0.75);
  39546. }
  39547. }
  39548. area[verb](attribs);
  39549. area.startX = areaPath.xMap;
  39550. area.shiftUnit = options.step ? 2 : 1;
  39551. });
  39552. }
  39553. /**
  39554. * @private
  39555. */
  39556. getGraphPath(points) {
  39557. const getGraphPath = LineSeries.prototype.getGraphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, bottomPoints = [], graphPoints = [], seriesIndex = this.index, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  39558. yAxis.getThreshold(options.threshold)), connectNulls = pick(// #10574
  39559. options.connectNulls, stacking === 'percent'),
  39560. // To display null points in underlying stacked series, this
  39561. // series graph must be broken, and the area also fall down to
  39562. // fill the gap left by the null point. #2069
  39563. addDummyPoints = function (i, otherI, side) {
  39564. const point = points[i], stackedValues = stacking &&
  39565. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0;
  39566. let top, bottom, isNull = true;
  39567. if (cliffVal || nullVal) {
  39568. top = (nullVal ?
  39569. stackedValues[0] :
  39570. stackedValues[1]) + cliffVal;
  39571. bottom = stackedValues[0] + cliffVal;
  39572. isNull = !!nullVal;
  39573. }
  39574. else if (!stacking &&
  39575. points[otherI] &&
  39576. points[otherI].isNull) {
  39577. top = bottom = threshold;
  39578. }
  39579. // Add to the top and bottom line of the area
  39580. if (typeof top !== 'undefined') {
  39581. graphPoints.push({
  39582. plotX: plotX,
  39583. plotY: top === null ?
  39584. translatedThreshold :
  39585. yAxis.getThreshold(top),
  39586. isNull: isNull,
  39587. isCliff: true
  39588. });
  39589. bottomPoints.push({
  39590. plotX: plotX,
  39591. plotY: bottom === null ?
  39592. translatedThreshold :
  39593. yAxis.getThreshold(bottom),
  39594. doCurve: false // #1041, gaps in areaspline areas
  39595. });
  39596. }
  39597. };
  39598. let plotX, isNull, yBottom;
  39599. // Find what points to use
  39600. points = points || this.points;
  39601. // Fill in missing points
  39602. if (stacking) {
  39603. points = this.getStackPoints(points);
  39604. }
  39605. for (let i = 0, iEnd = points.length; i < iEnd; ++i) {
  39606. // Reset after series.update of stacking property (#12033)
  39607. if (!stacking) {
  39608. points[i].leftCliff = points[i].rightCliff =
  39609. points[i].leftNull = points[i].rightNull = void 0;
  39610. }
  39611. isNull = points[i].isNull;
  39612. plotX = pick(points[i].rectPlotX, points[i].plotX);
  39613. yBottom = stacking ?
  39614. pick(points[i].yBottom, translatedThreshold) :
  39615. translatedThreshold;
  39616. if (!isNull || connectNulls) {
  39617. if (!connectNulls) {
  39618. addDummyPoints(i, i - 1, 'left');
  39619. }
  39620. // Skip null point when stacking is false and connectNulls
  39621. // true
  39622. if (!(isNull && !stacking && connectNulls)) {
  39623. graphPoints.push(points[i]);
  39624. bottomPoints.push({
  39625. x: i,
  39626. plotX: plotX,
  39627. plotY: yBottom
  39628. });
  39629. }
  39630. if (!connectNulls) {
  39631. addDummyPoints(i, i + 1, 'right');
  39632. }
  39633. }
  39634. }
  39635. const topPath = getGraphPath.call(this, graphPoints, true, true);
  39636. bottomPoints.reversed = true;
  39637. const bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  39638. const firstBottomPoint = bottomPath[0];
  39639. if (firstBottomPoint && firstBottomPoint[0] === 'M') {
  39640. bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
  39641. }
  39642. const areaPath = topPath.concat(bottomPath);
  39643. if (areaPath.length) {
  39644. areaPath.push(['Z']);
  39645. }
  39646. // TODO: don't set leftCliff and rightCliff when connectNulls?
  39647. const graphPath = getGraphPath
  39648. .call(this, graphPoints, false, connectNulls);
  39649. areaPath.xMap = topPath.xMap;
  39650. this.areaPath = areaPath;
  39651. return graphPath;
  39652. }
  39653. /**
  39654. * Return an array of stacked points, where null and missing points are
  39655. * replaced by dummy points in order for gaps to be drawn correctly in
  39656. * stacks.
  39657. * @private
  39658. */
  39659. getStackPoints(points) {
  39660. const series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacking.stacks[this.stackKey], pointMap = {}, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, upOrDown = yAxis.options.reversedStacks ? 1 : -1, seriesIndex = yAxisSeries.indexOf(series);
  39661. points = points || this.points;
  39662. if (this.options.stacking) {
  39663. for (let i = 0; i < points.length; i++) {
  39664. // Reset after point update (#7326)
  39665. points[i].leftNull = points[i].rightNull = void 0;
  39666. // Create a map where we can quickly look up the points by
  39667. // their X values.
  39668. pointMap[points[i].x] = points[i];
  39669. }
  39670. // Sort the keys (#1651)
  39671. objectEach(stack, function (stackX, x) {
  39672. // nulled after switching between
  39673. // grouping and not (#1651, #2336)
  39674. if (stackX.total !== null) {
  39675. keys.push(x);
  39676. }
  39677. });
  39678. keys.sort(function (a, b) {
  39679. return a - b;
  39680. });
  39681. const visibleSeries = yAxisSeries.map((s) => s.visible);
  39682. keys.forEach(function (x, idx) {
  39683. let y = 0, stackPoint, stackedValues;
  39684. if (pointMap[x] && !pointMap[x].isNull) {
  39685. segment.push(pointMap[x]);
  39686. // Find left and right cliff. -1 goes left, 1 goes
  39687. // right.
  39688. [-1, 1].forEach(function (direction) {
  39689. const nullName = direction === 1 ?
  39690. 'rightNull' :
  39691. 'leftNull', cliffName = direction === 1 ?
  39692. 'rightCliff' :
  39693. 'leftCliff', otherStack = stack[keys[idx + direction]];
  39694. let cliff = 0;
  39695. // If there is a stack next to this one,
  39696. // to the left or to the right...
  39697. if (otherStack) {
  39698. let i = seriesIndex;
  39699. // Can go either up or down,
  39700. // depending on reversedStacks
  39701. while (i >= 0 && i < seriesLength) {
  39702. const si = yAxisSeries[i].index;
  39703. stackPoint = otherStack.points[si];
  39704. if (!stackPoint) {
  39705. // If the next point in this series is
  39706. // missing, mark the point with
  39707. // point.leftNull or point.rightNull = true.
  39708. if (si === series.index) {
  39709. pointMap[x][nullName] = true;
  39710. // If there are missing points in the next
  39711. // stack in any of the series below this
  39712. // one, we need to substract the missing
  39713. // values and add a hiatus to the left or
  39714. // right.
  39715. }
  39716. else if (visibleSeries[i]) {
  39717. stackedValues = stack[x].points[si];
  39718. if (stackedValues) {
  39719. cliff -= (stackedValues[1] -
  39720. stackedValues[0]);
  39721. }
  39722. }
  39723. }
  39724. // When reversedStacks is true, loop up,
  39725. // else loop down
  39726. i += upOrDown;
  39727. }
  39728. }
  39729. pointMap[x][cliffName] = cliff;
  39730. });
  39731. // There is no point for this X value in this series, so we
  39732. // insert a dummy point in order for the areas to be drawn
  39733. // correctly.
  39734. }
  39735. else {
  39736. // Loop down the stack to find the series below this
  39737. // one that has a value (#1991)
  39738. let i = seriesIndex;
  39739. while (i >= 0 && i < seriesLength) {
  39740. const si = yAxisSeries[i].index;
  39741. stackPoint = stack[x].points[si];
  39742. if (stackPoint) {
  39743. y = stackPoint[1];
  39744. break;
  39745. }
  39746. // When reversedStacks is true, loop up, else loop
  39747. // down
  39748. i += upOrDown;
  39749. }
  39750. y = pick(y, 0);
  39751. y = yAxis.translate(// #6272
  39752. y, 0, 1, 0, 1);
  39753. segment.push({
  39754. isNull: true,
  39755. plotX: xAxis.translate(// #6272
  39756. x, 0, 0, 0, 1),
  39757. x: x,
  39758. plotY: y,
  39759. yBottom: y
  39760. });
  39761. }
  39762. });
  39763. }
  39764. return segment;
  39765. }
  39766. }
  39767. /**
  39768. * The area series type.
  39769. *
  39770. * @sample {highcharts} highcharts/demo/area-basic/
  39771. * Area chart
  39772. * @sample {highstock} stock/demo/area/
  39773. * Area chart
  39774. *
  39775. * @extends plotOptions.line
  39776. * @excluding useOhlcData
  39777. * @product highcharts highstock
  39778. * @optionparent plotOptions.area
  39779. */
  39780. AreaSeries.defaultOptions = merge(LineSeries.defaultOptions, {
  39781. /**
  39782. * @see [fillColor](#plotOptions.area.fillColor)
  39783. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39784. *
  39785. * @apioption plotOptions.area.color
  39786. */
  39787. /**
  39788. * Fill color or gradient for the area. When `null`, the series' `color`
  39789. * is used with the series' `fillOpacity`.
  39790. *
  39791. * In styled mode, the fill color can be set with the `.highcharts-area`
  39792. * class name.
  39793. *
  39794. * @see [color](#plotOptions.area.color)
  39795. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39796. *
  39797. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  39798. * Null by default
  39799. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  39800. * Gradient
  39801. *
  39802. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39803. * @product highcharts highstock
  39804. * @apioption plotOptions.area.fillColor
  39805. */
  39806. /**
  39807. * Fill opacity for the area. When you set an explicit `fillColor`,
  39808. * the `fillOpacity` is not applied. Instead, you should define the
  39809. * opacity in the `fillColor` with an rgba color definition. The
  39810. * `fillOpacity` setting, also the default setting, overrides the alpha
  39811. * component of the `color` setting.
  39812. *
  39813. * In styled mode, the fill opacity can be set with the
  39814. * `.highcharts-area` class name.
  39815. *
  39816. * @see [color](#plotOptions.area.color)
  39817. * @see [fillColor](#plotOptions.area.fillColor)
  39818. *
  39819. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  39820. * Automatic fill color and fill opacity of 0.1
  39821. *
  39822. * @type {number}
  39823. * @default {highcharts} 0.75
  39824. * @default {highstock} 0.75
  39825. * @product highcharts highstock
  39826. * @apioption plotOptions.area.fillOpacity
  39827. */
  39828. /**
  39829. * A separate color for the graph line. By default the line takes the
  39830. * `color` of the series, but the lineColor setting allows setting a
  39831. * separate color for the line without altering the `fillColor`.
  39832. *
  39833. * In styled mode, the line stroke can be set with the
  39834. * `.highcharts-graph` class name.
  39835. *
  39836. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  39837. * Dark gray line
  39838. *
  39839. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39840. * @product highcharts highstock
  39841. * @apioption plotOptions.area.lineColor
  39842. */
  39843. /**
  39844. * A separate color for the negative part of the area.
  39845. *
  39846. * In styled mode, a negative color is set with the
  39847. * `.highcharts-negative` class name.
  39848. *
  39849. * @see [negativeColor](#plotOptions.area.negativeColor)
  39850. *
  39851. * @sample {highcharts} highcharts/css/series-negative-color/
  39852. * Negative color in styled mode
  39853. *
  39854. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39855. * @since 3.0
  39856. * @product highcharts
  39857. * @apioption plotOptions.area.negativeFillColor
  39858. */
  39859. /**
  39860. * Whether the whole area or just the line should respond to mouseover
  39861. * tooltips and other mouse or touch events.
  39862. *
  39863. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  39864. * Display the tooltip when the area is hovered
  39865. *
  39866. * @type {boolean}
  39867. * @default false
  39868. * @since 1.1.6
  39869. * @product highcharts highstock
  39870. * @apioption plotOptions.area.trackByArea
  39871. */
  39872. /**
  39873. * The Y axis value to serve as the base for the area, for
  39874. * distinguishing between values above and below a threshold. The area
  39875. * between the graph and the threshold is filled.
  39876. *
  39877. * * If a number is given, the Y axis will scale to the threshold.
  39878. * * If `null`, the scaling behaves like a line series with fill between
  39879. * the graph and the Y axis minimum.
  39880. * * If `Infinity` or `-Infinity`, the area between the graph and the
  39881. * corresponding Y axis extreme is filled (since v6.1.0).
  39882. *
  39883. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  39884. * A threshold of 100
  39885. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  39886. * A threshold of Infinity
  39887. *
  39888. * @type {number|null}
  39889. * @since 2.0
  39890. * @product highcharts highstock
  39891. */
  39892. threshold: 0,
  39893. legendSymbol: 'rectangle'
  39894. });
  39895. extend(AreaSeries.prototype, {
  39896. singleStacks: false
  39897. });
  39898. SeriesRegistry.registerSeriesType('area', AreaSeries);
  39899. /* *
  39900. *
  39901. * Default Export
  39902. *
  39903. * */
  39904. /* *
  39905. *
  39906. * API Options
  39907. *
  39908. * */
  39909. /**
  39910. * A `area` series. If the [type](#series.area.type) option is not
  39911. * specified, it is inherited from [chart.type](#chart.type).
  39912. *
  39913. * @extends series,plotOptions.area
  39914. * @excluding dataParser, dataURL, useOhlcData
  39915. * @product highcharts highstock
  39916. * @apioption series.area
  39917. */
  39918. /**
  39919. * @see [fillColor](#series.area.fillColor)
  39920. * @see [fillOpacity](#series.area.fillOpacity)
  39921. *
  39922. * @apioption series.area.color
  39923. */
  39924. /**
  39925. * An array of data points for the series. For the `area` series type,
  39926. * points can be given in the following ways:
  39927. *
  39928. * 1. An array of numerical values. In this case, the numerical values will be
  39929. * interpreted as `y` options. The `x` values will be automatically
  39930. * calculated, either starting at 0 and incremented by 1, or from
  39931. * `pointStart` * and `pointInterval` given in the series options. If the
  39932. * axis has categories, these will be used. Example:
  39933. * ```js
  39934. * data: [0, 5, 3, 5]
  39935. * ```
  39936. *
  39937. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39938. * `x,y`. If the first value is a string, it is applied as the name of the
  39939. * point, and the `x` value is inferred.
  39940. * ```js
  39941. * data: [
  39942. * [0, 9],
  39943. * [1, 7],
  39944. * [2, 6]
  39945. * ]
  39946. * ```
  39947. *
  39948. * 3. An array of objects with named values. The following snippet shows only a
  39949. * few settings, see the complete options set below. If the total number of
  39950. * data points exceeds the series'
  39951. * [turboThreshold](#series.area.turboThreshold), this option is not
  39952. * available.
  39953. * ```js
  39954. * data: [{
  39955. * x: 1,
  39956. * y: 9,
  39957. * name: "Point2",
  39958. * color: "#00FF00"
  39959. * }, {
  39960. * x: 1,
  39961. * y: 6,
  39962. * name: "Point1",
  39963. * color: "#FF00FF"
  39964. * }]
  39965. * ```
  39966. *
  39967. * @sample {highcharts} highcharts/chart/reflow-true/
  39968. * Numerical values
  39969. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39970. * Arrays of numeric x and y
  39971. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39972. * Arrays of datetime x and y
  39973. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39974. * Arrays of point.name and y
  39975. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39976. * Config objects
  39977. *
  39978. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39979. * @extends series.line.data
  39980. * @product highcharts highstock
  39981. * @apioption series.area.data
  39982. */
  39983. /**
  39984. * @see [color](#series.area.color)
  39985. * @see [fillOpacity](#series.area.fillOpacity)
  39986. *
  39987. * @apioption series.area.fillColor
  39988. */
  39989. /**
  39990. * @see [color](#series.area.color)
  39991. * @see [fillColor](#series.area.fillColor)
  39992. *
  39993. * @default {highcharts} 0.75
  39994. * @default {highstock} 0.75
  39995. * @apioption series.area.fillOpacity
  39996. */
  39997. ''; // adds doclets above to transpilat
  39998. return AreaSeries;
  39999. });
  40000. _registerModule(_modules, 'Series/Spline/SplineSeries.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  40001. /* *
  40002. *
  40003. * (c) 2010-2021 Torstein Honsi
  40004. *
  40005. * License: www.highcharts.com/license
  40006. *
  40007. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40008. *
  40009. * */
  40010. const { line: LineSeries } = SeriesRegistry.seriesTypes;
  40011. const { merge, pick } = U;
  40012. /* *
  40013. *
  40014. * Class
  40015. *
  40016. * */
  40017. /**
  40018. * Spline series type.
  40019. *
  40020. * @private
  40021. */
  40022. class SplineSeries extends LineSeries {
  40023. constructor() {
  40024. /* *
  40025. *
  40026. * Static Properties
  40027. *
  40028. * */
  40029. super(...arguments);
  40030. /* *
  40031. *
  40032. * Properties
  40033. *
  40034. * */
  40035. this.data = void 0;
  40036. this.options = void 0;
  40037. this.points = void 0;
  40038. /* eslint-enable valid-jsdoc */
  40039. }
  40040. /* *
  40041. *
  40042. * Functions
  40043. *
  40044. * */
  40045. /* eslint-disable valid-jsdoc */
  40046. /**
  40047. * Get the spline segment from a given point's previous neighbour to the
  40048. * given point.
  40049. *
  40050. * @private
  40051. * @function Highcharts.seriesTypes.spline#getPointSpline
  40052. */
  40053. getPointSpline(points, point, i) {
  40054. const
  40055. // 1 means control points midway between points, 2 means 1/3
  40056. // from the point, 3 is 1/4 etc
  40057. smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1];
  40058. let leftContX, leftContY, rightContX, rightContY;
  40059. /**
  40060. * @private
  40061. */
  40062. function doCurve(otherPoint) {
  40063. return otherPoint &&
  40064. !otherPoint.isNull &&
  40065. otherPoint.doCurve !== false &&
  40066. // #6387, area splines next to null:
  40067. !point.isCliff;
  40068. }
  40069. // Find control points
  40070. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  40071. const lastX = lastPoint.plotX || 0, lastY = lastPoint.plotY || 0, nextX = nextPoint.plotX || 0, nextY = nextPoint.plotY || 0;
  40072. let correction = 0;
  40073. leftContX = (smoothing * plotX + lastX) / denom;
  40074. leftContY = (smoothing * plotY + lastY) / denom;
  40075. rightContX = (smoothing * plotX + nextX) / denom;
  40076. rightContY = (smoothing * plotY + nextY) / denom;
  40077. // Have the two control points make a straight line through main
  40078. // point
  40079. if (rightContX !== leftContX) { // #5016, division by zero
  40080. correction = (((rightContY - leftContY) *
  40081. (rightContX - plotX)) /
  40082. (rightContX - leftContX) + plotY - rightContY);
  40083. }
  40084. leftContY += correction;
  40085. rightContY += correction;
  40086. // to prevent false extremes, check that control points are
  40087. // between neighbouring points' y values
  40088. if (leftContY > lastY && leftContY > plotY) {
  40089. leftContY = Math.max(lastY, plotY);
  40090. // mirror of left control point
  40091. rightContY = 2 * plotY - leftContY;
  40092. }
  40093. else if (leftContY < lastY && leftContY < plotY) {
  40094. leftContY = Math.min(lastY, plotY);
  40095. rightContY = 2 * plotY - leftContY;
  40096. }
  40097. if (rightContY > nextY && rightContY > plotY) {
  40098. rightContY = Math.max(nextY, plotY);
  40099. leftContY = 2 * plotY - rightContY;
  40100. }
  40101. else if (rightContY < nextY && rightContY < plotY) {
  40102. rightContY = Math.min(nextY, plotY);
  40103. leftContY = 2 * plotY - rightContY;
  40104. }
  40105. // record for drawing in next point
  40106. point.rightContX = rightContX;
  40107. point.rightContY = rightContY;
  40108. }
  40109. // Visualize control points for debugging
  40110. /*
  40111. if (leftContX) {
  40112. this.chart.renderer.circle(
  40113. leftContX + this.chart.plotLeft,
  40114. leftContY + this.chart.plotTop,
  40115. 2
  40116. )
  40117. .attr({
  40118. stroke: 'red',
  40119. 'stroke-width': 2,
  40120. fill: 'none',
  40121. zIndex: 9
  40122. })
  40123. .add();
  40124. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  40125. leftContY + this.chart.plotTop,
  40126. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40127. .attr({
  40128. stroke: 'red',
  40129. 'stroke-width': 2,
  40130. zIndex: 9
  40131. })
  40132. .add();
  40133. }
  40134. if (rightContX) {
  40135. this.chart.renderer.circle(
  40136. rightContX + this.chart.plotLeft,
  40137. rightContY + this.chart.plotTop,
  40138. 2
  40139. )
  40140. .attr({
  40141. stroke: 'green',
  40142. 'stroke-width': 2,
  40143. fill: 'none',
  40144. zIndex: 9
  40145. })
  40146. .add();
  40147. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  40148. rightContY + this.chart.plotTop,
  40149. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40150. .attr({
  40151. stroke: 'green',
  40152. 'stroke-width': 2,
  40153. zIndex: 9
  40154. })
  40155. .add();
  40156. }
  40157. // */
  40158. const ret = [
  40159. 'C',
  40160. pick(lastPoint.rightContX, lastPoint.plotX, 0),
  40161. pick(lastPoint.rightContY, lastPoint.plotY, 0),
  40162. pick(leftContX, plotX, 0),
  40163. pick(leftContY, plotY, 0),
  40164. plotX,
  40165. plotY
  40166. ];
  40167. // reset for updating series later
  40168. lastPoint.rightContX = lastPoint.rightContY = void 0;
  40169. return ret;
  40170. }
  40171. }
  40172. /**
  40173. * A spline series is a special type of line series, where the segments
  40174. * between the data points are smoothed.
  40175. *
  40176. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  40177. * Spline chart
  40178. * @sample {highstock} stock/demo/spline/
  40179. * Spline chart
  40180. *
  40181. * @extends plotOptions.series
  40182. * @excluding step, boostThreshold, boostBlending
  40183. * @product highcharts highstock
  40184. * @optionparent plotOptions.spline
  40185. */
  40186. SplineSeries.defaultOptions = merge(LineSeries.defaultOptions);
  40187. SeriesRegistry.registerSeriesType('spline', SplineSeries);
  40188. /* *
  40189. *
  40190. * Default Export
  40191. *
  40192. * */
  40193. /* *
  40194. *
  40195. * API Options
  40196. *
  40197. * */
  40198. /**
  40199. * A `spline` series. If the [type](#series.spline.type) option is
  40200. * not specified, it is inherited from [chart.type](#chart.type).
  40201. *
  40202. * @extends series,plotOptions.spline
  40203. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40204. * @product highcharts highstock
  40205. * @apioption series.spline
  40206. */
  40207. /**
  40208. * An array of data points for the series. For the `spline` series type,
  40209. * points can be given in the following ways:
  40210. *
  40211. * 1. An array of numerical values. In this case, the numerical values will be
  40212. * interpreted as `y` options. The `x` values will be automatically
  40213. * calculated, either starting at 0 and incremented by 1, or from
  40214. * `pointStart` and `pointInterval` given in the series options. If the axis
  40215. * has categories, these will be used. Example:
  40216. * ```js
  40217. * data: [0, 5, 3, 5]
  40218. * ```
  40219. *
  40220. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40221. * `x,y`. If the first value is a string, it is applied as the name of the
  40222. * point, and the `x` value is inferred.
  40223. * ```js
  40224. * data: [
  40225. * [0, 9],
  40226. * [1, 2],
  40227. * [2, 8]
  40228. * ]
  40229. * ```
  40230. *
  40231. * 3. An array of objects with named values. The following snippet shows only a
  40232. * few settings, see the complete options set below. If the total number of
  40233. * data points exceeds the series'
  40234. * [turboThreshold](#series.spline.turboThreshold),
  40235. * this option is not available.
  40236. * ```js
  40237. * data: [{
  40238. * x: 1,
  40239. * y: 9,
  40240. * name: "Point2",
  40241. * color: "#00FF00"
  40242. * }, {
  40243. * x: 1,
  40244. * y: 0,
  40245. * name: "Point1",
  40246. * color: "#FF00FF"
  40247. * }]
  40248. * ```
  40249. *
  40250. * @sample {highcharts} highcharts/chart/reflow-true/
  40251. * Numerical values
  40252. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40253. * Arrays of numeric x and y
  40254. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40255. * Arrays of datetime x and y
  40256. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40257. * Arrays of point.name and y
  40258. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40259. * Config objects
  40260. *
  40261. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40262. * @extends series.line.data
  40263. * @product highcharts highstock
  40264. * @apioption series.spline.data
  40265. */
  40266. ''; // adds doclets above intro transpilat
  40267. return SplineSeries;
  40268. });
  40269. _registerModule(_modules, 'Series/AreaSpline/AreaSplineSeries.js', [_modules['Series/Spline/SplineSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SplineSeries, SeriesRegistry, U) {
  40270. /* *
  40271. *
  40272. * (c) 2010-2021 Torstein Honsi
  40273. *
  40274. * License: www.highcharts.com/license
  40275. *
  40276. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40277. *
  40278. * */
  40279. const { area: AreaSeries, area: { prototype: areaProto } } = SeriesRegistry.seriesTypes;
  40280. const { extend, merge } = U;
  40281. /* *
  40282. *
  40283. * Class
  40284. *
  40285. * */
  40286. /**
  40287. * AreaSpline series type.
  40288. *
  40289. * @private
  40290. * @class
  40291. * @name Highcharts.seriesTypes.areaspline
  40292. *
  40293. * @augments Highcharts.Series
  40294. */
  40295. class AreaSplineSeries extends SplineSeries {
  40296. constructor() {
  40297. /* *
  40298. *
  40299. * Static Properties
  40300. *
  40301. * */
  40302. super(...arguments);
  40303. /* *
  40304. *
  40305. * Properties
  40306. *
  40307. * */
  40308. this.data = void 0;
  40309. this.points = void 0;
  40310. this.options = void 0;
  40311. }
  40312. }
  40313. AreaSplineSeries.defaultOptions = merge(SplineSeries.defaultOptions, AreaSeries.defaultOptions);
  40314. extend(AreaSplineSeries.prototype, {
  40315. getGraphPath: areaProto.getGraphPath,
  40316. getStackPoints: areaProto.getStackPoints,
  40317. drawGraph: areaProto.drawGraph
  40318. });
  40319. SeriesRegistry.registerSeriesType('areaspline', AreaSplineSeries);
  40320. /* *
  40321. *
  40322. * Default Export
  40323. *
  40324. * */
  40325. /* *
  40326. *
  40327. * API Options
  40328. *
  40329. * */
  40330. /**
  40331. * The area spline series is an area series where the graph between the
  40332. * points is smoothed into a spline.
  40333. *
  40334. * @sample {highcharts} highcharts/demo/areaspline/
  40335. * Area spline chart
  40336. * @sample {highstock} stock/demo/areaspline/
  40337. * Area spline chart
  40338. *
  40339. * @extends plotOptions.area
  40340. * @excluding step, boostThreshold, boostBlending
  40341. * @product highcharts highstock
  40342. * @apioption plotOptions.areaspline
  40343. */
  40344. /**
  40345. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40346. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40347. *
  40348. * @apioption plotOptions.areaspline.color
  40349. */
  40350. /**
  40351. * @see [color](#plotOptions.areaspline.color)
  40352. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40353. *
  40354. * @apioption plotOptions.areaspline.fillColor
  40355. */
  40356. /**
  40357. * @see [color](#plotOptions.areaspline.color)
  40358. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40359. *
  40360. * @default 0.75
  40361. * @apioption plotOptions.areaspline.fillOpacity
  40362. */
  40363. /**
  40364. * A `areaspline` series. If the [type](#series.areaspline.type) option
  40365. * is not specified, it is inherited from [chart.type](#chart.type).
  40366. *
  40367. *
  40368. * @extends series,plotOptions.areaspline
  40369. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40370. * @product highcharts highstock
  40371. * @apioption series.areaspline
  40372. */
  40373. /**
  40374. * @see [fillColor](#series.areaspline.fillColor)
  40375. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40376. *
  40377. * @apioption series.areaspline.color
  40378. */
  40379. /**
  40380. * An array of data points for the series. For the `areaspline` series
  40381. * type, points can be given in the following ways:
  40382. *
  40383. * 1. An array of numerical values. In this case, the numerical values will be
  40384. * interpreted as `y` options. The `x` values will be automatically
  40385. * calculated, either starting at 0 and incremented by 1, or from
  40386. * `pointStart` and `pointInterval` given in the series options. If the axis
  40387. * has categories, these will be used. Example:
  40388. * ```js
  40389. * data: [0, 5, 3, 5]
  40390. * ```
  40391. *
  40392. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40393. * `x,y`. If the first value is a string, it is applied as the name of the
  40394. * point, and the `x` value is inferred.
  40395. * ```js
  40396. * data: [
  40397. * [0, 10],
  40398. * [1, 9],
  40399. * [2, 3]
  40400. * ]
  40401. * ```
  40402. *
  40403. * 3. An array of objects with named values. The following snippet shows only a
  40404. * few settings, see the complete options set below. If the total number of
  40405. * data points exceeds the series'
  40406. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  40407. * available.
  40408. * ```js
  40409. * data: [{
  40410. * x: 1,
  40411. * y: 4,
  40412. * name: "Point2",
  40413. * color: "#00FF00"
  40414. * }, {
  40415. * x: 1,
  40416. * y: 4,
  40417. * name: "Point1",
  40418. * color: "#FF00FF"
  40419. * }]
  40420. * ```
  40421. *
  40422. * @sample {highcharts} highcharts/chart/reflow-true/
  40423. * Numerical values
  40424. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40425. * Arrays of numeric x and y
  40426. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40427. * Arrays of datetime x and y
  40428. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40429. * Arrays of point.name and y
  40430. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40431. * Config objects
  40432. *
  40433. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40434. * @extends series.line.data
  40435. * @product highcharts highstock
  40436. * @apioption series.areaspline.data
  40437. */
  40438. /**
  40439. * @see [color](#series.areaspline.color)
  40440. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40441. *
  40442. * @apioption series.areaspline.fillColor
  40443. */
  40444. /**
  40445. * @see [color](#series.areaspline.color)
  40446. * @see [fillColor](#series.areaspline.fillColor)
  40447. *
  40448. * @default 0.75
  40449. * @apioption series.areaspline.fillOpacity
  40450. */
  40451. ''; // adds doclets above into transpilat
  40452. return AreaSplineSeries;
  40453. });
  40454. _registerModule(_modules, 'Series/Column/ColumnSeriesDefaults.js', [], function () {
  40455. /* *
  40456. *
  40457. * (c) 2010-2021 Torstein Honsi
  40458. *
  40459. * License: www.highcharts.com/license
  40460. *
  40461. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40462. *
  40463. * */
  40464. /* *
  40465. *
  40466. * API Options
  40467. *
  40468. * */
  40469. /**
  40470. * Column series display one column per value along an X axis.
  40471. *
  40472. * @sample {highcharts} highcharts/demo/column-basic/
  40473. * Column chart
  40474. * @sample {highstock} stock/demo/column/
  40475. * Column chart
  40476. *
  40477. * @extends plotOptions.line
  40478. * @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
  40479. * lineWidth, marker, step, useOhlcData
  40480. * @product highcharts highstock
  40481. * @optionparent plotOptions.column
  40482. */
  40483. const ColumnSeriesDefaults = {
  40484. /**
  40485. * The corner radius of the border surrounding each column or bar. A number
  40486. * signifies pixels. A percentage string, like for example `50%`, signifies
  40487. * a relative size. For columns this is relative to the column width, for
  40488. * pies it is relative to the radius and the inner radius.
  40489. *
  40490. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  40491. * Rounded columns
  40492. * @sample highcharts/plotoptions/series-border-radius
  40493. * Column and pie with rounded border
  40494. *
  40495. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  40496. * @product highcharts highstock gantt
  40497. */
  40498. borderRadius: 3,
  40499. /**
  40500. * When using automatic point colors pulled from the global
  40501. * [colors](colors) or series-specific
  40502. * [plotOptions.column.colors](series.colors) collections, this option
  40503. * determines whether the chart should receive one color per series or
  40504. * one color per point.
  40505. *
  40506. * In styled mode, the `colors` or `series.colors` arrays are not
  40507. * supported, and instead this option gives the points individual color
  40508. * class names on the form `highcharts-color-{n}`.
  40509. *
  40510. * @see [series colors](#plotOptions.column.colors)
  40511. *
  40512. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  40513. * False by default
  40514. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  40515. * True
  40516. *
  40517. * @type {boolean}
  40518. * @default false
  40519. * @since 2.0
  40520. * @product highcharts highstock gantt
  40521. * @apioption plotOptions.column.colorByPoint
  40522. */
  40523. /**
  40524. * A series specific or series type specific color set to apply instead
  40525. * of the global [colors](#colors) when [colorByPoint](
  40526. * #plotOptions.column.colorByPoint) is true.
  40527. *
  40528. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  40529. * @since 3.0
  40530. * @product highcharts highstock gantt
  40531. * @apioption plotOptions.column.colors
  40532. */
  40533. /**
  40534. * When `true`, the columns will center in the category, ignoring null
  40535. * or missing points. When `false`, space will be reserved for null or
  40536. * missing points.
  40537. *
  40538. * @sample {highcharts} highcharts/series-column/centerincategory/
  40539. * Center in category
  40540. *
  40541. * @since 8.0.1
  40542. * @product highcharts highstock gantt
  40543. */
  40544. centerInCategory: false,
  40545. /**
  40546. * Padding between each value groups, in x axis units.
  40547. *
  40548. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  40549. * 0.2 by default
  40550. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  40551. * No group padding - all columns are evenly spaced
  40552. *
  40553. * @product highcharts highstock gantt
  40554. */
  40555. groupPadding: 0.2,
  40556. /**
  40557. * Whether to group non-stacked columns or to let them render
  40558. * independent of each other. Non-grouped columns will be laid out
  40559. * individually and overlap each other.
  40560. *
  40561. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  40562. * Grouping disabled
  40563. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  40564. * Grouping disabled
  40565. *
  40566. * @type {boolean}
  40567. * @default true
  40568. * @since 2.3.0
  40569. * @product highcharts highstock gantt
  40570. * @apioption plotOptions.column.grouping
  40571. */
  40572. /** @ignore-option */
  40573. marker: null,
  40574. /**
  40575. * The maximum allowed pixel width for a column, translated to the
  40576. * height of a bar in a bar chart. This prevents the columns from
  40577. * becoming too wide when there is a small number of points in the
  40578. * chart.
  40579. *
  40580. * @see [pointWidth](#plotOptions.column.pointWidth)
  40581. *
  40582. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  40583. * Limited to 50
  40584. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  40585. * Limited to 50
  40586. *
  40587. * @type {number}
  40588. * @since 4.1.8
  40589. * @product highcharts highstock gantt
  40590. * @apioption plotOptions.column.maxPointWidth
  40591. */
  40592. /**
  40593. * Padding between each column or bar, in x axis units.
  40594. *
  40595. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  40596. * 0.1 by default
  40597. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  40598. * 0.25
  40599. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  40600. * 0 for tightly packed columns
  40601. *
  40602. * @product highcharts highstock gantt
  40603. */
  40604. pointPadding: 0.1,
  40605. /**
  40606. * A pixel value specifying a fixed width for each column or bar point.
  40607. * When set to `undefined`, the width is calculated from the
  40608. * `pointPadding` and `groupPadding`. The width effects the dimension
  40609. * that is not based on the point value. For column series it is the
  40610. * hoizontal length and for bar series it is the vertical length.
  40611. *
  40612. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  40613. *
  40614. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  40615. * 20px wide columns regardless of chart width or the amount of
  40616. * data points
  40617. *
  40618. * @type {number}
  40619. * @since 1.2.5
  40620. * @product highcharts highstock gantt
  40621. * @apioption plotOptions.column.pointWidth
  40622. */
  40623. /**
  40624. * A pixel value specifying a fixed width for the column or bar.
  40625. * Overrides pointWidth on the series.
  40626. *
  40627. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40628. *
  40629. * @type {number}
  40630. * @default undefined
  40631. * @since 7.0.0
  40632. * @product highcharts highstock gantt
  40633. * @apioption series.column.data.pointWidth
  40634. */
  40635. /**
  40636. * The minimal height for a column or width for a bar. By default,
  40637. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  40638. * set the minimal point length to a pixel value like 3\. In stacked
  40639. * column charts, minPointLength might not be respected for tightly
  40640. * packed values.
  40641. *
  40642. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  40643. * Zero base value
  40644. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  40645. * Positive and negative close to zero values
  40646. *
  40647. * @product highcharts highstock gantt
  40648. */
  40649. minPointLength: 0,
  40650. /**
  40651. * When the series contains less points than the crop threshold, all
  40652. * points are drawn, event if the points fall outside the visible plot
  40653. * area at the current zoom. The advantage of drawing all points
  40654. * (including markers and columns), is that animation is performed on
  40655. * updates. On the other hand, when the series contains more points than
  40656. * the crop threshold, the series data is cropped to only contain points
  40657. * that fall within the plot area. The advantage of cropping away
  40658. * invisible points is to increase performance on large series.
  40659. *
  40660. * @product highcharts highstock gantt
  40661. */
  40662. cropThreshold: 50,
  40663. /**
  40664. * The X axis range that each point is valid for. This determines the
  40665. * width of the column. On a categorized axis, the range will be 1
  40666. * by default (one category unit). On linear and datetime axes, the
  40667. * range will be computed as the distance between the two closest data
  40668. * points.
  40669. *
  40670. * The default `null` means it is computed automatically, but this
  40671. * option can be used to override the automatic value.
  40672. *
  40673. * This option is set by default to 1 if data sorting is enabled.
  40674. *
  40675. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  40676. * Set the point range to one day on a data set with one week
  40677. * between the points
  40678. *
  40679. * @type {number|null}
  40680. * @since 2.3
  40681. * @product highcharts highstock gantt
  40682. */
  40683. pointRange: null,
  40684. states: {
  40685. /**
  40686. * Options for the hovered point. These settings override the normal
  40687. * state options when a point is moused over or touched.
  40688. *
  40689. * @extends plotOptions.series.states.hover
  40690. * @excluding halo, lineWidth, lineWidthPlus, marker
  40691. * @product highcharts highstock gantt
  40692. */
  40693. hover: {
  40694. /** @ignore-option */
  40695. halo: false,
  40696. /**
  40697. * A specific border color for the hovered point. Defaults to
  40698. * inherit the normal state border color.
  40699. *
  40700. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40701. * @product highcharts gantt
  40702. * @apioption plotOptions.column.states.hover.borderColor
  40703. */
  40704. /**
  40705. * A specific color for the hovered point.
  40706. *
  40707. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40708. * @product highcharts gantt
  40709. * @apioption plotOptions.column.states.hover.color
  40710. */
  40711. /**
  40712. * How much to brighten the point on interaction. Requires the
  40713. * main color to be defined in hex or rgb(a) format.
  40714. *
  40715. * In styled mode, the hover brightening is by default replaced
  40716. * with a fill-opacity set in the `.highcharts-point:hover`
  40717. * rule.
  40718. *
  40719. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  40720. * Brighten by 0.5
  40721. *
  40722. * @product highcharts highstock gantt
  40723. */
  40724. brightness: 0.1
  40725. },
  40726. /**
  40727. * Options for the selected point. These settings override the
  40728. * normal state options when a point is selected.
  40729. *
  40730. * @extends plotOptions.series.states.select
  40731. * @excluding halo, lineWidth, lineWidthPlus, marker
  40732. * @product highcharts highstock gantt
  40733. */
  40734. select: {
  40735. /**
  40736. * A specific color for the selected point.
  40737. *
  40738. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40739. * @default #cccccc
  40740. * @product highcharts highstock gantt
  40741. */
  40742. color: "#cccccc" /* Palette.neutralColor20 */,
  40743. /**
  40744. * A specific border color for the selected point.
  40745. *
  40746. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40747. * @default #000000
  40748. * @product highcharts highstock gantt
  40749. */
  40750. borderColor: "#000000" /* Palette.neutralColor100 */
  40751. }
  40752. },
  40753. dataLabels: {
  40754. align: void 0,
  40755. verticalAlign: void 0,
  40756. /**
  40757. * The y position offset of the label relative to the point in
  40758. * pixels.
  40759. *
  40760. * @type {number}
  40761. */
  40762. y: void 0
  40763. },
  40764. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  40765. /** @ignore-option */
  40766. startFromThreshold: true,
  40767. stickyTracking: false,
  40768. tooltip: {
  40769. distance: 6
  40770. },
  40771. /**
  40772. * The Y axis value to serve as the base for the columns, for
  40773. * distinguishing between values above and below a threshold. If `null`,
  40774. * the columns extend from the padding Y axis minimum.
  40775. *
  40776. * @type {number|null}
  40777. * @since 2.0
  40778. * @product highcharts
  40779. */
  40780. threshold: 0,
  40781. /**
  40782. * The width of the border surrounding each column or bar. Defaults to
  40783. * `1` when there is room for a border, but to `0` when the columns are
  40784. * so dense that a border would cover the next column.
  40785. *
  40786. * In styled mode, the stroke width can be set with the
  40787. * `.highcharts-point` rule.
  40788. *
  40789. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40790. * 2px black border
  40791. *
  40792. * @type {number}
  40793. * @default undefined
  40794. * @product highcharts highstock gantt
  40795. * @apioption plotOptions.column.borderWidth
  40796. */
  40797. /**
  40798. * The color of the border surrounding each column or bar.
  40799. *
  40800. * In styled mode, the border stroke can be set with the
  40801. * `.highcharts-point` rule.
  40802. *
  40803. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40804. * Dark gray border
  40805. *
  40806. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40807. * @default #ffffff
  40808. * @product highcharts highstock gantt
  40809. */
  40810. borderColor: "#ffffff" /* Palette.backgroundColor */
  40811. };
  40812. /**
  40813. * A `column` series. If the [type](#series.column.type) option is
  40814. * not specified, it is inherited from [chart.type](#chart.type).
  40815. *
  40816. * @extends series,plotOptions.column
  40817. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  40818. * lineWidth, marker, connectEnds, step
  40819. * @product highcharts highstock
  40820. * @apioption series.column
  40821. */
  40822. /**
  40823. * An array of data points for the series. For the `column` series type,
  40824. * points can be given in the following ways:
  40825. *
  40826. * 1. An array of numerical values. In this case, the numerical values will be
  40827. * interpreted as `y` options. The `x` values will be automatically
  40828. * calculated, either starting at 0 and incremented by 1, or from
  40829. * `pointStart` and `pointInterval` given in the series options. If the axis
  40830. * has categories, these will be used. Example:
  40831. * ```js
  40832. * data: [0, 5, 3, 5]
  40833. * ```
  40834. *
  40835. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40836. * `x,y`. If the first value is a string, it is applied as the name of the
  40837. * point, and the `x` value is inferred.
  40838. * ```js
  40839. * data: [
  40840. * [0, 6],
  40841. * [1, 2],
  40842. * [2, 6]
  40843. * ]
  40844. * ```
  40845. *
  40846. * 3. An array of objects with named values. The following snippet shows only a
  40847. * few settings, see the complete options set below. If the total number of
  40848. * data points exceeds the series'
  40849. * [turboThreshold](#series.column.turboThreshold), this option is not
  40850. * available.
  40851. * ```js
  40852. * data: [{
  40853. * x: 1,
  40854. * y: 9,
  40855. * name: "Point2",
  40856. * color: "#00FF00"
  40857. * }, {
  40858. * x: 1,
  40859. * y: 6,
  40860. * name: "Point1",
  40861. * color: "#FF00FF"
  40862. * }]
  40863. * ```
  40864. *
  40865. * @sample {highcharts} highcharts/chart/reflow-true/
  40866. * Numerical values
  40867. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40868. * Arrays of numeric x and y
  40869. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40870. * Arrays of datetime x and y
  40871. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40872. * Arrays of point.name and y
  40873. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40874. * Config objects
  40875. *
  40876. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40877. * @extends series.line.data
  40878. * @excluding marker
  40879. * @product highcharts highstock
  40880. * @apioption series.column.data
  40881. */
  40882. /**
  40883. * The color of the border surrounding the column or bar.
  40884. *
  40885. * In styled mode, the border stroke can be set with the `.highcharts-point`
  40886. * rule.
  40887. *
  40888. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40889. * Dark gray border
  40890. *
  40891. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40892. * @product highcharts highstock
  40893. * @apioption series.column.data.borderColor
  40894. */
  40895. /**
  40896. * The width of the border surrounding the column or bar.
  40897. *
  40898. * In styled mode, the stroke width can be set with the `.highcharts-point`
  40899. * rule.
  40900. *
  40901. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40902. * 2px black border
  40903. *
  40904. * @type {number}
  40905. * @product highcharts highstock
  40906. * @apioption series.column.data.borderWidth
  40907. */
  40908. /**
  40909. * A name for the dash style to use for the column or bar. Overrides
  40910. * dashStyle on the series.
  40911. *
  40912. * In styled mode, the stroke dash-array can be set with the same classes as
  40913. * listed under [data.color](#series.column.data.color).
  40914. *
  40915. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  40916. *
  40917. * @type {Highcharts.DashStyleValue}
  40918. * @apioption series.column.data.dashStyle
  40919. */
  40920. /**
  40921. * A pixel value specifying a fixed width for the column or bar. Overrides
  40922. * pointWidth on the series. The width effects the dimension that is not based
  40923. * on the point value.
  40924. *
  40925. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40926. *
  40927. * @type {number}
  40928. * @apioption series.column.data.pointWidth
  40929. */
  40930. /**
  40931. * @excluding halo, lineWidth, lineWidthPlus, marker
  40932. * @product highcharts highstock
  40933. * @apioption series.column.states.hover
  40934. */
  40935. /**
  40936. * @excluding halo, lineWidth, lineWidthPlus, marker
  40937. * @product highcharts highstock
  40938. * @apioption series.column.states.select
  40939. */
  40940. ''; // keeps doclets above in JS file
  40941. /* *
  40942. *
  40943. * Default Export
  40944. *
  40945. * */
  40946. return ColumnSeriesDefaults;
  40947. });
  40948. _registerModule(_modules, 'Series/Column/ColumnSeries.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Color/Color.js'], _modules['Series/Column/ColumnSeriesDefaults.js'], _modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (A, Color, ColumnSeriesDefaults, H, Series, SeriesRegistry, U) {
  40949. /* *
  40950. *
  40951. * (c) 2010-2021 Torstein Honsi
  40952. *
  40953. * License: www.highcharts.com/license
  40954. *
  40955. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40956. *
  40957. * */
  40958. const { animObject } = A;
  40959. const { parse: color } = Color;
  40960. const { hasTouch, noop } = H;
  40961. const { clamp, defined, extend, fireEvent, isArray, isNumber, merge, pick, objectEach, relativeLength } = U;
  40962. /* *
  40963. *
  40964. * Class
  40965. *
  40966. * */
  40967. /**
  40968. * The column series type.
  40969. *
  40970. * @private
  40971. * @class
  40972. * @name Highcharts.seriesTypes.column
  40973. *
  40974. * @augments Highcharts.Series
  40975. */
  40976. class ColumnSeries extends Series {
  40977. constructor() {
  40978. /* *
  40979. *
  40980. * Static Properties
  40981. *
  40982. * */
  40983. super(...arguments);
  40984. /* *
  40985. *
  40986. * Properties
  40987. *
  40988. * */
  40989. this.borderWidth = void 0;
  40990. this.data = void 0;
  40991. this.group = void 0;
  40992. this.options = void 0;
  40993. this.points = void 0;
  40994. /* eslint-enable valid-jsdoc */
  40995. }
  40996. /* *
  40997. *
  40998. * Functions
  40999. *
  41000. * */
  41001. /* eslint-disable valid-jsdoc */
  41002. /**
  41003. * Animate the column heights one by one from zero.
  41004. *
  41005. * @private
  41006. * @function Highcharts.seriesTypes.column#animate
  41007. *
  41008. * @param {boolean} init
  41009. * Whether to initialize the animation or run it
  41010. */
  41011. animate(init) {
  41012. const series = this, yAxis = this.yAxis, yAxisPos = yAxis.pos, options = series.options, inverted = this.chart.inverted, attr = {}, translateProp = inverted ?
  41013. 'translateX' :
  41014. 'translateY';
  41015. let translateStart, translatedThreshold;
  41016. if (init) {
  41017. attr.scaleY = 0.001;
  41018. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxisPos, yAxisPos + yAxis.len);
  41019. if (inverted) {
  41020. attr.translateX = translatedThreshold - yAxis.len;
  41021. }
  41022. else {
  41023. attr.translateY = translatedThreshold;
  41024. }
  41025. // apply finnal clipping (used in Highcharts Stock) (#7083)
  41026. // animation is done by scaleY, so cliping is for panes
  41027. if (series.clipBox) {
  41028. series.setClip();
  41029. }
  41030. series.group.attr(attr);
  41031. }
  41032. else { // run the animation
  41033. translateStart = Number(series.group.attr(translateProp));
  41034. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  41035. // Do the scale synchronously to ensure smooth
  41036. // updating (#5030, #7228)
  41037. step: function (val, fx) {
  41038. if (series.group) {
  41039. attr[translateProp] = translateStart +
  41040. fx.pos * (yAxisPos - translateStart);
  41041. series.group.attr(attr);
  41042. }
  41043. }
  41044. }));
  41045. }
  41046. }
  41047. /**
  41048. * Initialize the series. Extends the basic Series.init method by
  41049. * marking other series of the same type as dirty.
  41050. *
  41051. * @private
  41052. * @function Highcharts.seriesTypes.column#init
  41053. */
  41054. init(chart, options) {
  41055. super.init.apply(this, arguments);
  41056. const series = this;
  41057. chart = series.chart;
  41058. // if the series is added dynamically, force redraw of other
  41059. // series affected by a new column
  41060. if (chart.hasRendered) {
  41061. chart.series.forEach(function (otherSeries) {
  41062. if (otherSeries.type === series.type) {
  41063. otherSeries.isDirty = true;
  41064. }
  41065. });
  41066. }
  41067. }
  41068. /**
  41069. * Return the width and x offset of the columns adjusted for grouping,
  41070. * groupPadding, pointPadding, pointWidth etc.
  41071. *
  41072. * @private
  41073. * @function Highcharts.seriesTypes.column#getColumnMetrics
  41074. */
  41075. getColumnMetrics() {
  41076. const series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks,
  41077. // Keep backward compatibility: reversed xAxis had reversed
  41078. // stacks
  41079. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  41080. (!xAxis.reversed && reversedStacks), stackGroups = {};
  41081. let stackKey, columnCount = 0;
  41082. // Get the total number of column type series. This is called on
  41083. // every series. Consider moving this logic to a chart.orderStacks()
  41084. // function and call it on init, addSeries and removeSeries
  41085. if (options.grouping === false) {
  41086. columnCount = 1;
  41087. }
  41088. else {
  41089. series.chart.series.forEach(function (otherSeries) {
  41090. const otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options;
  41091. let columnIndex;
  41092. if (otherSeries.type === series.type &&
  41093. (otherSeries.visible ||
  41094. !series.chart.options.chart.ignoreHiddenSeries) &&
  41095. yAxis.len === otherYAxis.len &&
  41096. yAxis.pos === otherYAxis.pos) { // #642, #2086
  41097. if (otherOptions.stacking &&
  41098. otherOptions.stacking !== 'group') {
  41099. stackKey = otherSeries.stackKey;
  41100. if (typeof stackGroups[stackKey] ===
  41101. 'undefined') {
  41102. stackGroups[stackKey] = columnCount++;
  41103. }
  41104. columnIndex = stackGroups[stackKey];
  41105. }
  41106. else if (otherOptions.grouping !== false) { // #1162
  41107. columnIndex = columnCount++;
  41108. }
  41109. otherSeries.columnIndex = columnIndex;
  41110. }
  41111. });
  41112. }
  41113. const categoryWidth = Math.min(Math.abs(xAxis.transA) * ((xAxis.ordinal && xAxis.ordinal.slope) ||
  41114. options.pointRange ||
  41115. xAxis.closestPointRange ||
  41116. xAxis.tickInterval ||
  41117. 1), // #2610
  41118. xAxis.len // #1535
  41119. ), groupPadding = categoryWidth * options.groupPadding, groupWidth = categoryWidth - 2 * groupPadding, pointOffsetWidth = groupWidth / (columnCount || 1), pointWidth = Math.min(options.maxPointWidth || xAxis.len, pick(options.pointWidth, pointOffsetWidth * (1 - 2 * options.pointPadding))), pointPadding = (pointOffsetWidth - pointWidth) / 2,
  41120. // #1251, #3737
  41121. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
  41122. (groupPadding +
  41123. colIndex * pointOffsetWidth -
  41124. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  41125. // Save it for reading in linked series (Error bars particularly)
  41126. series.columnMetrics = {
  41127. width: pointWidth,
  41128. offset: pointXOffset,
  41129. paddedWidth: pointOffsetWidth,
  41130. columnCount
  41131. };
  41132. return series.columnMetrics;
  41133. }
  41134. /**
  41135. * Make the columns crisp. The edges are rounded to the nearest full
  41136. * pixel.
  41137. *
  41138. * @private
  41139. * @function Highcharts.seriesTypes.column#crispCol
  41140. */
  41141. crispCol(x, y, w, h) {
  41142. const chart = this.chart, borderWidth = this.borderWidth, xCrisp = -(borderWidth % 2 ? 0.5 : 0);
  41143. let right, yCrisp = borderWidth % 2 ? 0.5 : 1;
  41144. // Horizontal. We need to first compute the exact right edge, then
  41145. // round it and compute the width from there.
  41146. if (this.options.crisp) {
  41147. right = Math.round(x + w) + xCrisp;
  41148. x = Math.round(x) + xCrisp;
  41149. w = right - x;
  41150. }
  41151. // Vertical
  41152. const bottom = Math.round(y + h) + yCrisp, fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  41153. y = Math.round(y) + yCrisp;
  41154. h = bottom - y;
  41155. // Top edges are exceptions
  41156. if (fromTop && h) { // #5146
  41157. y -= 1;
  41158. h += 1;
  41159. }
  41160. return {
  41161. x: x,
  41162. y: y,
  41163. width: w,
  41164. height: h
  41165. };
  41166. }
  41167. /**
  41168. * Adjust for missing columns, according to the `centerInCategory`
  41169. * option. Missing columns are either single points or stacks where the
  41170. * point or points are either missing or null.
  41171. *
  41172. * @private
  41173. * @function Highcharts.seriesTypes.column#adjustForMissingColumns
  41174. * @param {number} x
  41175. * The x coordinate of the column, left side
  41176. *
  41177. * @param {number} pointWidth
  41178. * The pointWidth, already computed upstream
  41179. *
  41180. * @param {Highcharts.ColumnPoint} point
  41181. * The point instance
  41182. *
  41183. * @param {Highcharts.ColumnMetricsObject} metrics
  41184. * The series-wide column metrics
  41185. *
  41186. * @return {number}
  41187. * The adjusted x position, or the original if not adjusted
  41188. */
  41189. adjustForMissingColumns(x, pointWidth, point, metrics) {
  41190. const stacking = this.options.stacking;
  41191. if (!point.isNull && metrics.columnCount > 1) {
  41192. const reversedStacks = this.yAxis.options.reversedStacks;
  41193. let indexInCategory = 0, totalInCategory = reversedStacks ? 0 : -metrics.columnCount;
  41194. // Loop over all the stacks on the Y axis. When stacking is enabled,
  41195. // these are real point stacks. When stacking is not enabled, but
  41196. // `centerInCategory` is true, there is one stack handling the
  41197. // grouping of points in each category. This is done in the
  41198. // `setGroupedPoints` function.
  41199. objectEach(this.yAxis.stacking && this.yAxis.stacking.stacks, (stack) => {
  41200. if (typeof point.x === 'number') {
  41201. const stackItem = stack[point.x.toString()];
  41202. if (stackItem) {
  41203. const pointValues = stackItem.points[this.index];
  41204. // If true `stacking` is enabled, count the total
  41205. // number of non-null stacks in the category, and
  41206. // note which index this point is within those
  41207. // stacks.
  41208. if (stacking) {
  41209. if (pointValues) {
  41210. indexInCategory = totalInCategory;
  41211. }
  41212. if (stackItem.hasValidPoints) {
  41213. reversedStacks ? // #16169
  41214. totalInCategory++ : totalInCategory--;
  41215. }
  41216. // If `stacking` is not enabled, look for the index
  41217. }
  41218. else if (isArray(pointValues)) {
  41219. // If there are multiple points with the same X
  41220. // then gather all series in category, and
  41221. // assign index
  41222. let seriesIndexes = Object
  41223. .keys(stackItem.points)
  41224. .filter((pointKey) =>
  41225. // Filter out duplicate X's
  41226. !pointKey.match(',') &&
  41227. // Filter out null points
  41228. stackItem.points[pointKey] &&
  41229. stackItem.points[pointKey].length > 1)
  41230. .map(parseFloat)
  41231. .sort((a, b) => b - a);
  41232. indexInCategory = seriesIndexes.indexOf(this.index);
  41233. totalInCategory = seriesIndexes.length;
  41234. }
  41235. }
  41236. }
  41237. });
  41238. // Compute the adjusted x position
  41239. const boxWidth = (totalInCategory - 1) * metrics.paddedWidth +
  41240. pointWidth;
  41241. x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
  41242. indexInCategory * metrics.paddedWidth;
  41243. }
  41244. return x;
  41245. }
  41246. /**
  41247. * Translate each point to the plot area coordinate system and find
  41248. * shape positions
  41249. *
  41250. * @private
  41251. * @function Highcharts.seriesTypes.column#translate
  41252. */
  41253. translate() {
  41254. const series = this, chart = series.chart, options = series.options, dense = series.dense =
  41255. series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
  41256. ), xAxis = series.xAxis, yAxis = series.yAxis, threshold = options.threshold, minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), seriesPointWidth = metrics.width, seriesXOffset = series.pointXOffset = metrics.offset, dataMin = series.dataMin, dataMax = series.dataMax;
  41257. // postprocessed for border width
  41258. let seriesBarW = series.barW =
  41259. Math.max(seriesPointWidth, 1 + 2 * borderWidth), translatedThreshold = series.translatedThreshold =
  41260. yAxis.getThreshold(threshold);
  41261. if (chart.inverted) {
  41262. translatedThreshold -= 0.5; // #3355
  41263. }
  41264. // When the pointPadding is 0, we want the columns to be packed
  41265. // tightly, so we allow individual columns to have individual sizes.
  41266. // When pointPadding is greater, we strive for equal-width columns
  41267. // (#2694).
  41268. if (options.pointPadding) {
  41269. seriesBarW = Math.ceil(seriesBarW);
  41270. }
  41271. Series.prototype.translate.apply(series);
  41272. // Record the new values
  41273. series.points.forEach(function (point) {
  41274. const yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), plotX = point.plotX || 0,
  41275. // Don't draw too far outside plot area (#1303, #2241,
  41276. // #4264)
  41277. plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance), stackBox = point.stackBox;
  41278. let up, barY = Math.min(plotY, yBottom), barH = Math.max(plotY, yBottom) - barY, pointWidth = seriesPointWidth, barX = plotX + seriesXOffset, barW = seriesBarW;
  41279. // Handle options.minPointLength
  41280. if (minPointLength && Math.abs(barH) < minPointLength) {
  41281. barH = minPointLength;
  41282. up = (!yAxis.reversed && !point.negative) ||
  41283. (yAxis.reversed && point.negative);
  41284. // Reverse zeros if there's no positive value in the series
  41285. // in visible range (#7046)
  41286. if (isNumber(threshold) &&
  41287. isNumber(dataMax) &&
  41288. point.y === threshold &&
  41289. dataMax <= threshold &&
  41290. // and if there's room for it (#7311)
  41291. (yAxis.min || 0) < threshold &&
  41292. // if all points are the same value (i.e zero) not draw
  41293. // as negative points (#10646), but only if there's room
  41294. // for it (#14876)
  41295. (dataMin !== dataMax || (yAxis.max || 0) <= threshold)) {
  41296. up = !up;
  41297. point.negative = !point.negative;
  41298. }
  41299. // If stacked...
  41300. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  41301. // ...keep position
  41302. yBottom - minPointLength :
  41303. // #1485, #4051
  41304. translatedThreshold -
  41305. (up ? minPointLength : 0));
  41306. }
  41307. // Handle point.options.pointWidth
  41308. // @todo Handle grouping/stacking too. Calculate offset properly
  41309. if (defined(point.options.pointWidth)) {
  41310. pointWidth = barW =
  41311. Math.ceil(point.options.pointWidth);
  41312. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  41313. }
  41314. // Adjust for null or missing points
  41315. if (options.centerInCategory) {
  41316. barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
  41317. }
  41318. // Cache for access in polar
  41319. point.barX = barX;
  41320. point.pointWidth = pointWidth;
  41321. // Fix the tooltip on center of grouped columns (#1216, #424,
  41322. // #3648)
  41323. point.tooltipPos = chart.inverted ?
  41324. [
  41325. clamp(yAxis.len + yAxis.pos - chart.plotLeft - plotY, yAxis.pos - chart.plotLeft, yAxis.len + yAxis.pos - chart.plotLeft),
  41326. xAxis.len + xAxis.pos - chart.plotTop - barX - barW / 2,
  41327. barH
  41328. ] :
  41329. [
  41330. xAxis.left - chart.plotLeft + barX + barW / 2,
  41331. clamp(plotY + yAxis.pos -
  41332. chart.plotTop, yAxis.pos - chart.plotTop, yAxis.len + yAxis.pos - chart.plotTop),
  41333. barH
  41334. ];
  41335. // Register shape type and arguments to be used in drawPoints. Allow
  41336. // `shapeType` defined on `pointClass` level.
  41337. point.shapeType = series.pointClass.prototype.shapeType ||
  41338. 'roundedRect';
  41339. point.shapeArgs = series.crispCol(barX,
  41340. // #3169, drilldown from null must have a position to work from.
  41341. // #6585, dataLabel should be placed on xAxis, not floating in
  41342. // the middle of the chart.
  41343. point.isNull ? translatedThreshold : barY, barW, point.isNull ? 0 : barH);
  41344. });
  41345. // Fire a specific event after column translate. We could instead apply
  41346. // all the column logic in an `afterTranslate` event handler, but there
  41347. // are so many other series types that use the column translation, that
  41348. // it is more convenient to have a specific event for it.
  41349. fireEvent(this, 'afterColumnTranslate');
  41350. }
  41351. /**
  41352. * Columns have no graph
  41353. *
  41354. * @private
  41355. * @function Highcharts.seriesTypes.column#drawGraph
  41356. */
  41357. drawGraph() {
  41358. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  41359. }
  41360. /**
  41361. * Get presentational attributes
  41362. *
  41363. * @private
  41364. * @function Highcharts.seriesTypes.column#pointAttribs
  41365. */
  41366. pointAttribs(point, state) {
  41367. const options = this.options, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth';
  41368. let stateOptions, zone, brightness, fill = (point && point.color) || this.color,
  41369. // set to fill when borderColor null:
  41370. stroke = ((point && point[strokeOption]) ||
  41371. options[strokeOption] ||
  41372. fill), dashstyle = (point && point.options.dashStyle) || options.dashStyle, strokeWidth = (point && point[strokeWidthOption]) ||
  41373. options[strokeWidthOption] ||
  41374. this[strokeWidthOption] || 0, opacity = pick(point && point.opacity, options.opacity, 1);
  41375. // Handle zone colors
  41376. if (point && this.zones.length) {
  41377. zone = point.getZone();
  41378. // When zones are present, don't use point.color (#4267).
  41379. // Changed order (#6527), added support for colorAxis (#10670)
  41380. fill = (point.options.color ||
  41381. (zone && (zone.color || point.nonZonedColor)) ||
  41382. this.color);
  41383. if (zone) {
  41384. stroke = zone.borderColor || stroke;
  41385. dashstyle = zone.dashStyle || dashstyle;
  41386. strokeWidth = zone.borderWidth || strokeWidth;
  41387. }
  41388. }
  41389. // Select or hover states
  41390. if (state && point) {
  41391. stateOptions = merge(options.states[state],
  41392. // #6401
  41393. point.options.states &&
  41394. point.options.states[state] ||
  41395. {});
  41396. brightness = stateOptions.brightness;
  41397. fill =
  41398. stateOptions.color || (typeof brightness !== 'undefined' &&
  41399. color(fill)
  41400. .brighten(stateOptions.brightness)
  41401. .get()) || fill;
  41402. stroke = stateOptions[strokeOption] || stroke;
  41403. strokeWidth =
  41404. stateOptions[strokeWidthOption] || strokeWidth;
  41405. dashstyle = stateOptions.dashStyle || dashstyle;
  41406. opacity = pick(stateOptions.opacity, opacity);
  41407. }
  41408. const ret = {
  41409. fill: fill,
  41410. stroke: stroke,
  41411. 'stroke-width': strokeWidth,
  41412. opacity: opacity
  41413. };
  41414. if (dashstyle) {
  41415. ret.dashstyle = dashstyle;
  41416. }
  41417. return ret;
  41418. }
  41419. /**
  41420. * Draw the columns. For bars, the series.group is rotated, so the same
  41421. * coordinates apply for columns and bars. This method is inherited by
  41422. * scatter series.
  41423. *
  41424. * @private
  41425. * @function Highcharts.seriesTypes.column#drawPoints
  41426. */
  41427. drawPoints(points = this.points) {
  41428. const series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250;
  41429. let shapeArgs;
  41430. // draw the columns
  41431. points.forEach(function (point) {
  41432. const plotY = point.plotY;
  41433. let graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ?
  41434. 'animate' : 'attr';
  41435. if (isNumber(plotY) && point.y !== null) {
  41436. shapeArgs = point.shapeArgs;
  41437. // When updating a series between 2d and 3d or cartesian and
  41438. // polar, the shape type changes.
  41439. if (graphic && point.hasNewShapeType()) {
  41440. graphic = graphic.destroy();
  41441. }
  41442. // Set starting position for point sliding animation.
  41443. if (series.enabledDataSorting) {
  41444. point.startXPos = series.xAxis.reversed ?
  41445. -(shapeArgs ? (shapeArgs.width || 0) : 0) :
  41446. series.xAxis.width;
  41447. }
  41448. if (!graphic) {
  41449. point.graphic = graphic =
  41450. renderer[point.shapeType](shapeArgs)
  41451. .add(point.group || series.group);
  41452. if (graphic &&
  41453. series.enabledDataSorting &&
  41454. chart.hasRendered &&
  41455. chart.pointCount < animationLimit) {
  41456. graphic.attr({
  41457. x: point.startXPos
  41458. });
  41459. hasGraphic = true;
  41460. verb = 'animate';
  41461. }
  41462. }
  41463. if (graphic && hasGraphic) { // update
  41464. graphic[verb](merge(shapeArgs));
  41465. }
  41466. // Presentational
  41467. if (!chart.styledMode) {
  41468. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  41469. .shadow(point.allowShadow !== false && options.shadow);
  41470. }
  41471. if (graphic) {
  41472. graphic.addClass(point.getClassName(), true);
  41473. graphic.attr({
  41474. visibility: point.visible ? 'inherit' : 'hidden'
  41475. });
  41476. }
  41477. }
  41478. else if (graphic) {
  41479. point.graphic = graphic.destroy(); // #1269
  41480. }
  41481. });
  41482. }
  41483. /**
  41484. * Draw the tracker for a point.
  41485. * @private
  41486. */
  41487. drawTracker(points = this.points) {
  41488. const series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) {
  41489. const point = pointer.getPointFromEvent(e);
  41490. // undefined on graph in scatterchart
  41491. if (typeof point !== 'undefined' &&
  41492. series.options.enableMouseTracking) {
  41493. pointer.isDirectTouch = true;
  41494. point.onMouseOver(e);
  41495. }
  41496. };
  41497. let dataLabels;
  41498. // Add reference to the point
  41499. points.forEach(function (point) {
  41500. dataLabels = (isArray(point.dataLabels) ?
  41501. point.dataLabels :
  41502. (point.dataLabel ? [point.dataLabel] : []));
  41503. if (point.graphic) {
  41504. point.graphic.element.point = point;
  41505. }
  41506. dataLabels.forEach(function (dataLabel) {
  41507. if (dataLabel.div) {
  41508. dataLabel.div.point = point;
  41509. }
  41510. else {
  41511. dataLabel.element.point = point;
  41512. }
  41513. });
  41514. });
  41515. // Add the event listeners, we need to do this only once
  41516. if (!series._hasTracking) {
  41517. series.trackerGroups.forEach(function (key) {
  41518. if (series[key]) {
  41519. // we don't always have dataLabelsGroup
  41520. series[key]
  41521. .addClass('highcharts-tracker')
  41522. .on('mouseover', onMouseOver)
  41523. .on('mouseout', function (e) {
  41524. pointer.onTrackerMouseOut(e);
  41525. });
  41526. if (hasTouch) {
  41527. series[key].on('touchstart', onMouseOver);
  41528. }
  41529. if (!chart.styledMode && series.options.cursor) {
  41530. series[key]
  41531. .css({ cursor: series.options.cursor });
  41532. }
  41533. }
  41534. });
  41535. series._hasTracking = true;
  41536. }
  41537. fireEvent(this, 'afterDrawTracker');
  41538. }
  41539. /**
  41540. * Remove this series from the chart
  41541. *
  41542. * @private
  41543. * @function Highcharts.seriesTypes.column#remove
  41544. */
  41545. remove() {
  41546. const series = this, chart = series.chart;
  41547. // column and bar series affects other series of the same type
  41548. // as they are either stacked or grouped
  41549. if (chart.hasRendered) {
  41550. chart.series.forEach(function (otherSeries) {
  41551. if (otherSeries.type === series.type) {
  41552. otherSeries.isDirty = true;
  41553. }
  41554. });
  41555. }
  41556. Series.prototype.remove.apply(series, arguments);
  41557. }
  41558. }
  41559. ColumnSeries.defaultOptions = merge(Series.defaultOptions, ColumnSeriesDefaults);
  41560. extend(ColumnSeries.prototype, {
  41561. cropShoulder: 0,
  41562. // When tooltip is not shared, this series (and derivatives) requires
  41563. // direct touch/hover. KD-tree does not apply.
  41564. directTouch: true,
  41565. getSymbol: noop,
  41566. // use separate negative stacks, unlike area stacks where a negative
  41567. // point is substracted from previous (#1910)
  41568. negStacks: true,
  41569. trackerGroups: ['group', 'dataLabelsGroup']
  41570. });
  41571. SeriesRegistry.registerSeriesType('column', ColumnSeries);
  41572. /* *
  41573. *
  41574. * Default Export
  41575. *
  41576. * */
  41577. /* *
  41578. *
  41579. * API Declarations
  41580. *
  41581. * */
  41582. /**
  41583. * Adjusted width and x offset of the columns for grouping.
  41584. *
  41585. * @private
  41586. * @interface Highcharts.ColumnMetricsObject
  41587. */ /**
  41588. * Width of the columns.
  41589. * @name Highcharts.ColumnMetricsObject#width
  41590. * @type {number}
  41591. */ /**
  41592. * Offset of the columns.
  41593. * @name Highcharts.ColumnMetricsObject#offset
  41594. * @type {number}
  41595. */
  41596. ''; // detach doclets above
  41597. return ColumnSeries;
  41598. });
  41599. _registerModule(_modules, 'Core/Series/DataLabel.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (A, F, U) {
  41600. /* *
  41601. *
  41602. * (c) 2010-2021 Torstein Honsi
  41603. *
  41604. * License: www.highcharts.com/license
  41605. *
  41606. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41607. *
  41608. * */
  41609. const { getDeferredAnimation } = A;
  41610. const { format } = F;
  41611. const { defined, extend, fireEvent, isArray, isString, merge, objectEach, pick, splat } = U;
  41612. /* *
  41613. *
  41614. * Composition
  41615. *
  41616. * */
  41617. /* eslint-disable valid-jsdoc */
  41618. var DataLabel;
  41619. (function (DataLabel) {
  41620. /* *
  41621. *
  41622. * Declarations
  41623. *
  41624. * */
  41625. /* *
  41626. *
  41627. * Constants
  41628. *
  41629. * */
  41630. const composedMembers = [];
  41631. /* *
  41632. *
  41633. * Functions
  41634. *
  41635. * */
  41636. /* eslint-disable valid-jsdoc */
  41637. /**
  41638. * Align each individual data label.
  41639. * @private
  41640. */
  41641. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  41642. const series = this, chart = this.chart, inverted = this.isCartesian && chart.inverted, enabledDataSorting = this.enabledDataSorting, plotX = point.plotX, plotY = point.plotY, rotation = options.rotation, align = options.align, isInsidePlot = defined(plotX) &&
  41643. defined(plotY) &&
  41644. chart.isInsidePlot(plotX, Math.round(plotY), {
  41645. inverted,
  41646. paneCoordinates: true,
  41647. series
  41648. }), setStartPos = (alignOptions) => {
  41649. if (enabledDataSorting && series.xAxis && !justify) {
  41650. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  41651. }
  41652. };
  41653. let baseline, rotCorr, // rotation correction
  41654. // Math.round for rounding errors (#2683), alignTo to allow column
  41655. // labels (#2700)
  41656. alignAttr, // the final position;
  41657. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  41658. point.visible !== false &&
  41659. defined(plotX) &&
  41660. (point.series.forceDL ||
  41661. (enabledDataSorting && !justify) ||
  41662. isInsidePlot ||
  41663. (
  41664. // If the data label is inside the align box, it is
  41665. // enough that parts of the align box is inside the
  41666. // plot area (#12370). When stacking, it is always
  41667. // inside regardless of the option (#15148).
  41668. pick(options.inside, !!this.options.stacking) &&
  41669. alignTo &&
  41670. chart.isInsidePlot(plotX, inverted ?
  41671. alignTo.x + 1 :
  41672. alignTo.y + alignTo.height - 1, {
  41673. inverted,
  41674. paneCoordinates: true,
  41675. series
  41676. })));
  41677. const pos = point.pos();
  41678. if (visible && pos) {
  41679. if (rotation) {
  41680. dataLabel.attr({ align });
  41681. }
  41682. let bBox = dataLabel.getBBox(true), bBoxCorrection = [0, 0];
  41683. baseline = chart.renderer.fontMetrics(dataLabel).b;
  41684. // The alignment box is a singular point
  41685. alignTo = extend({
  41686. x: pos[0],
  41687. y: Math.round(pos[1]),
  41688. width: 0,
  41689. height: 0
  41690. }, alignTo);
  41691. // Add the text size for alignment calculation
  41692. extend(options, {
  41693. width: bBox.width,
  41694. height: bBox.height
  41695. });
  41696. // Allow a hook for changing alignment in the last moment, then do
  41697. // the alignment
  41698. if (rotation) {
  41699. justify = false; // Not supported for rotated text
  41700. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  41701. alignAttr = {
  41702. x: (alignTo.x +
  41703. (options.x || 0) +
  41704. alignTo.width / 2 +
  41705. rotCorr.x),
  41706. y: (alignTo.y +
  41707. (options.y || 0) +
  41708. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  41709. alignTo.height)
  41710. };
  41711. bBoxCorrection = [
  41712. bBox.x - Number(dataLabel.attr('x')),
  41713. bBox.y - Number(dataLabel.attr('y'))
  41714. ];
  41715. setStartPos(alignAttr); // data sorting
  41716. dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
  41717. }
  41718. else {
  41719. setStartPos(alignTo); // data sorting
  41720. dataLabel.align(options, void 0, alignTo);
  41721. alignAttr = dataLabel.alignAttr;
  41722. }
  41723. // Handle justify or crop
  41724. if (justify && alignTo.height >= 0) { // #8830
  41725. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  41726. // Now check that the data label is within the plot area
  41727. }
  41728. else if (pick(options.crop, true)) {
  41729. let { x, y } = alignAttr;
  41730. x += bBoxCorrection[0];
  41731. y += bBoxCorrection[1];
  41732. // Uncomment this block to visualize the bounding boxes used for
  41733. // determining visibility
  41734. /*
  41735. chart.renderer.rect(
  41736. chart.plotLeft + alignAttr.x + bBox.x,
  41737. chart.plotTop + alignAttr.y + bBox.y + 9999,
  41738. bBox.width,
  41739. bBox.height
  41740. ).attr({
  41741. stroke: 'rgba(0, 0, 0, 0.3)',
  41742. 'stroke-width': 0.5
  41743. }).add();
  41744. chart.renderer.circle(
  41745. chart.plotLeft + alignAttr.x,
  41746. chart.plotTop + alignAttr.y,
  41747. 2
  41748. ).attr({
  41749. fill: 'red',
  41750. zIndex: 20
  41751. }).add();
  41752. // */
  41753. visible =
  41754. chart.isInsidePlot(x, y, {
  41755. paneCoordinates: true,
  41756. series
  41757. }) &&
  41758. chart.isInsidePlot(x + bBox.width, y + bBox.height, {
  41759. paneCoordinates: true,
  41760. series
  41761. });
  41762. }
  41763. // When we're using a shape, make it possible with a connector or an
  41764. // arrow pointing to thie point
  41765. if (options.shape && !rotation) {
  41766. dataLabel[isNew ? 'attr' : 'animate']({
  41767. anchorX: pos[0],
  41768. anchorY: pos[1]
  41769. });
  41770. }
  41771. }
  41772. // To use alignAttr property in hideOverlappingLabels
  41773. if (isNew && enabledDataSorting) {
  41774. dataLabel.placed = false;
  41775. }
  41776. // Show or hide based on the final aligned position
  41777. if (!visible && (!enabledDataSorting || justify)) {
  41778. dataLabel.hide();
  41779. dataLabel.placed = false; // don't animate back in
  41780. }
  41781. else {
  41782. dataLabel.show();
  41783. }
  41784. }
  41785. /**
  41786. * Handle the dataLabels.filter option.
  41787. * @private
  41788. */
  41789. function applyFilter(point, options) {
  41790. const filter = options.filter;
  41791. if (filter) {
  41792. const op = filter.operator;
  41793. const prop = point[filter.property];
  41794. const val = filter.value;
  41795. if ((op === '>' && prop > val) ||
  41796. (op === '<' && prop < val) ||
  41797. (op === '>=' && prop >= val) ||
  41798. (op === '<=' && prop <= val) ||
  41799. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  41800. (op === '===' && prop === val)) {
  41801. return true;
  41802. }
  41803. return false;
  41804. }
  41805. return true;
  41806. }
  41807. /**
  41808. * @private
  41809. */
  41810. function compose(SeriesClass) {
  41811. if (U.pushUnique(composedMembers, SeriesClass)) {
  41812. const seriesProto = SeriesClass.prototype;
  41813. seriesProto.initDataLabelsGroup = initDataLabelsGroup;
  41814. seriesProto.initDataLabels = initDataLabels;
  41815. seriesProto.alignDataLabel = alignDataLabel;
  41816. seriesProto.drawDataLabels = drawDataLabels;
  41817. seriesProto.justifyDataLabel = justifyDataLabel;
  41818. seriesProto.setDataLabelStartPos = setDataLabelStartPos;
  41819. }
  41820. }
  41821. DataLabel.compose = compose;
  41822. /**
  41823. * Create the SVGElement group for dataLabels
  41824. * @private
  41825. */
  41826. function initDataLabelsGroup() {
  41827. return this.plotGroup('dataLabelsGroup', 'data-labels', this.hasRendered ? 'inherit' : 'hidden', // #5133, #10220
  41828. this.options.dataLabels.zIndex || 6);
  41829. }
  41830. /**
  41831. * Init the data labels with the correct animation
  41832. * @private
  41833. */
  41834. function initDataLabels(animationConfig) {
  41835. const series = this, hasRendered = series.hasRendered || 0;
  41836. // Create a separate group for the data labels to avoid rotation
  41837. const dataLabelsGroup = this.initDataLabelsGroup()
  41838. .attr({ opacity: +hasRendered }); // #3300
  41839. if (!hasRendered && dataLabelsGroup) {
  41840. if (series.visible) { // #2597, #3023, #3024
  41841. dataLabelsGroup.show();
  41842. }
  41843. if (series.options.animation) {
  41844. dataLabelsGroup.animate({ opacity: 1 }, animationConfig);
  41845. }
  41846. else {
  41847. dataLabelsGroup.attr({ opacity: 1 });
  41848. }
  41849. }
  41850. return dataLabelsGroup;
  41851. }
  41852. /**
  41853. * Draw the data labels
  41854. * @private
  41855. */
  41856. function drawDataLabels(points = this.points) {
  41857. var _a,
  41858. _b;
  41859. const series = this, chart = series.chart, seriesOptions = series.options, renderer = chart.renderer, { backgroundColor, plotBackgroundColor } = chart.options.chart, plotOptions = chart.options.plotOptions, contrastColor = renderer.getContrast((isString(plotBackgroundColor) && plotBackgroundColor) ||
  41860. (isString(backgroundColor) && backgroundColor) ||
  41861. "#000000" /* Palette.neutralColor100 */);
  41862. let seriesDlOptions = seriesOptions.dataLabels, pointOptions, dataLabelsGroup;
  41863. const firstDLOptions = splat(seriesDlOptions)[0], dataLabelAnim = firstDLOptions.animation, animationConfig = firstDLOptions.defer ?
  41864. getDeferredAnimation(chart, dataLabelAnim, series) :
  41865. { defer: 0, duration: 0 };
  41866. // Merge in plotOptions.dataLabels for series
  41867. seriesDlOptions = mergeArrays(mergeArrays((_a = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions.series) === null || _a === void 0 ? void 0 : _a.dataLabels, (_b = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions[series.type]) === null || _b === void 0 ? void 0 : _b.dataLabels), seriesDlOptions);
  41868. fireEvent(this, 'drawDataLabels');
  41869. if (isArray(seriesDlOptions) ||
  41870. seriesDlOptions.enabled ||
  41871. series._hasPointLabels) {
  41872. dataLabelsGroup = this.initDataLabels(animationConfig);
  41873. // Make the labels for each point
  41874. points.forEach((point) => {
  41875. var _a;
  41876. const dataLabels = point.dataLabels || [];
  41877. // Merge in series options for the point.
  41878. // @note dataLabelAttribs (like pointAttribs) would eradicate
  41879. // the need for dlOptions, and simplify the section below.
  41880. pointOptions = splat(mergeArrays(seriesDlOptions,
  41881. // dlOptions is used in treemaps
  41882. point.dlOptions || ((_a = point.options) === null || _a === void 0 ? void 0 : _a.dataLabels)));
  41883. // Handle each individual data label for this point
  41884. pointOptions.forEach((labelOptions, i) => {
  41885. var _a;
  41886. // Options for one datalabel
  41887. const labelEnabled = (labelOptions.enabled &&
  41888. // #2282, #4641, #7112, #10049
  41889. (!point.isNull || point.dataLabelOnNull) &&
  41890. applyFilter(point, labelOptions)), connector = point.connectors ?
  41891. point.connectors[i] :
  41892. point.connector, style = labelOptions.style || {};
  41893. let labelConfig, formatString, labelText, rotation, attr = {}, dataLabel = dataLabels[i], isNew = !dataLabel;
  41894. const labelDistance = pick(labelOptions.distance, point.labelDistance);
  41895. if (labelEnabled) {
  41896. // Create individual options structure that can be
  41897. // extended without affecting others
  41898. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  41899. labelConfig = point.getLabelConfig();
  41900. labelText = defined(formatString) ?
  41901. format(formatString, labelConfig, chart) :
  41902. (labelOptions[point.formatPrefix + 'Formatter'] ||
  41903. labelOptions.formatter).call(labelConfig, labelOptions);
  41904. rotation = labelOptions.rotation;
  41905. if (!chart.styledMode) {
  41906. // Determine the color
  41907. style.color = pick(labelOptions.color, style.color, isString(series.color) ? series.color : void 0, "#000000" /* Palette.neutralColor100 */);
  41908. // Get automated contrast color
  41909. if (style.color === 'contrast') {
  41910. point.contrastColor = renderer.getContrast((point.color || series.color));
  41911. style.color = ((!defined(labelDistance) &&
  41912. labelOptions.inside) ||
  41913. (labelDistance || 0) < 0 ||
  41914. seriesOptions.stacking) ?
  41915. point.contrastColor :
  41916. contrastColor;
  41917. }
  41918. else {
  41919. delete point.contrastColor;
  41920. }
  41921. if (seriesOptions.cursor) {
  41922. style.cursor = seriesOptions.cursor;
  41923. }
  41924. }
  41925. attr = {
  41926. r: labelOptions.borderRadius || 0,
  41927. rotation,
  41928. padding: labelOptions.padding,
  41929. zIndex: 1
  41930. };
  41931. if (!chart.styledMode) {
  41932. const { backgroundColor, borderColor } = labelOptions;
  41933. attr.fill = backgroundColor === 'auto' ?
  41934. point.color :
  41935. backgroundColor;
  41936. attr.stroke = borderColor === 'auto' ?
  41937. point.color :
  41938. borderColor;
  41939. attr['stroke-width'] = labelOptions.borderWidth;
  41940. }
  41941. // Remove unused attributes (#947)
  41942. objectEach(attr, (val, name) => {
  41943. if (typeof val === 'undefined') {
  41944. delete attr[name];
  41945. }
  41946. });
  41947. }
  41948. // If the point is outside the plot area, or the label
  41949. // changes properties that we cannot change, destroy it and
  41950. // build a new one below. #678, #820.
  41951. if (dataLabel && (!labelEnabled ||
  41952. !defined(labelText) ||
  41953. !!dataLabel.div !== !!labelOptions.useHTML ||
  41954. (
  41955. // Change from no rotation to rotation and
  41956. // vice versa. Don't use defined() because
  41957. // rotation = 0 means also rotation = undefined
  41958. (!dataLabel.rotation ||
  41959. !labelOptions.rotation) &&
  41960. dataLabel.rotation !== labelOptions.rotation))) {
  41961. dataLabel = void 0;
  41962. isNew = true;
  41963. if (connector && point.connector) {
  41964. point.connector = point.connector.destroy();
  41965. if (point.connectors) {
  41966. // Remove point.connectors if this was the last
  41967. // one
  41968. if (point.connectors.length === 1) {
  41969. delete point.connectors;
  41970. }
  41971. else {
  41972. delete point.connectors[i];
  41973. }
  41974. }
  41975. }
  41976. }
  41977. // Individual labels are disabled if the are explicitly
  41978. // disabled in the point options, or if they fall outside
  41979. // the plot area.
  41980. if (labelEnabled && defined(labelText)) {
  41981. if (!dataLabel) {
  41982. // Create new label element
  41983. dataLabel = rotation ?
  41984. // Labels don't rotate, use text element
  41985. renderer.text(labelText, 0, 0, labelOptions.useHTML)
  41986. .addClass('highcharts-data-label') :
  41987. // We can use label
  41988. renderer.label(labelText, 0, 0, labelOptions.shape, void 0, void 0, labelOptions.useHTML, void 0, 'data-label');
  41989. if (dataLabel) {
  41990. dataLabel.addClass(' highcharts-data-label-color-' +
  41991. point.colorIndex +
  41992. ' ' + (labelOptions.className || '') +
  41993. ( // #3398
  41994. labelOptions.useHTML ?
  41995. ' highcharts-tracker' :
  41996. ''));
  41997. }
  41998. }
  41999. else {
  42000. // Use old element and just update text
  42001. attr.text = labelText;
  42002. }
  42003. // Store data label options for later access
  42004. if (dataLabel) {
  42005. dataLabel.options = labelOptions;
  42006. dataLabel.attr(attr);
  42007. if (!chart.styledMode) {
  42008. // Styles must be applied before add in order to
  42009. // read text bounding box
  42010. dataLabel.css(style).shadow(labelOptions.shadow);
  42011. }
  42012. const textPathOptions = labelOptions[point.formatPrefix + 'TextPath'] || labelOptions.textPath;
  42013. if (textPathOptions && !labelOptions.useHTML) {
  42014. dataLabel.setTextPath(((_a = point.getDataLabelPath) === null || _a === void 0 ? void 0 : _a.call(point, dataLabel)) ||
  42015. point.graphic, textPathOptions);
  42016. if (point.dataLabelPath &&
  42017. !textPathOptions.enabled) {
  42018. // clean the DOM
  42019. point.dataLabelPath = (point.dataLabelPath.destroy());
  42020. }
  42021. }
  42022. if (!dataLabel.added) {
  42023. dataLabel.add(dataLabelsGroup);
  42024. }
  42025. // Now the data label is created and placed at 0,0,
  42026. // so we need to align it
  42027. series.alignDataLabel(point, dataLabel, labelOptions, void 0, isNew);
  42028. dataLabel.isActive = true;
  42029. if (dataLabels[i] && dataLabels[i] !== dataLabel) {
  42030. dataLabels[i].destroy();
  42031. }
  42032. dataLabels[i] = dataLabel;
  42033. }
  42034. }
  42035. });
  42036. // Destroy and remove the inactive ones
  42037. let j = dataLabels.length;
  42038. while (j--) {
  42039. if (dataLabels[j].isActive) {
  42040. dataLabels[j].isActive = false;
  42041. }
  42042. else {
  42043. dataLabels[j].destroy();
  42044. dataLabels.splice(j, 1);
  42045. }
  42046. }
  42047. // Write back
  42048. point.dataLabel = dataLabels[0];
  42049. point.dataLabels = dataLabels;
  42050. });
  42051. }
  42052. fireEvent(this, 'afterDrawDataLabels');
  42053. }
  42054. /**
  42055. * If data labels fall partly outside the plot area, align them back in, in
  42056. * a way that doesn't hide the point.
  42057. * @private
  42058. */
  42059. function justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  42060. const chart = this.chart, align = options.align, verticalAlign = options.verticalAlign, padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  42061. let { x = 0, y = 0 } = options, off, justified;
  42062. // Off left
  42063. off = (alignAttr.x || 0) + padding;
  42064. if (off < 0) {
  42065. if (align === 'right' && x >= 0) {
  42066. options.align = 'left';
  42067. options.inside = true;
  42068. }
  42069. else {
  42070. x -= off;
  42071. }
  42072. justified = true;
  42073. }
  42074. // Off right
  42075. off = (alignAttr.x || 0) + bBox.width - padding;
  42076. if (off > chart.plotWidth) {
  42077. if (align === 'left' && x <= 0) {
  42078. options.align = 'right';
  42079. options.inside = true;
  42080. }
  42081. else {
  42082. x += chart.plotWidth - off;
  42083. }
  42084. justified = true;
  42085. }
  42086. // Off top
  42087. off = alignAttr.y + padding;
  42088. if (off < 0) {
  42089. if (verticalAlign === 'bottom' && y >= 0) {
  42090. options.verticalAlign = 'top';
  42091. options.inside = true;
  42092. }
  42093. else {
  42094. y -= off;
  42095. }
  42096. justified = true;
  42097. }
  42098. // Off bottom
  42099. off = (alignAttr.y || 0) + bBox.height - padding;
  42100. if (off > chart.plotHeight) {
  42101. if (verticalAlign === 'top' && y <= 0) {
  42102. options.verticalAlign = 'bottom';
  42103. options.inside = true;
  42104. }
  42105. else {
  42106. y += chart.plotHeight - off;
  42107. }
  42108. justified = true;
  42109. }
  42110. if (justified) {
  42111. options.x = x;
  42112. options.y = y;
  42113. dataLabel.placed = !isNew;
  42114. dataLabel.align(options, void 0, alignTo);
  42115. }
  42116. return justified;
  42117. }
  42118. /**
  42119. * Merge two objects that can be arrays. If one of them is an array, the
  42120. * other is merged into each element. If both are arrays, each element is
  42121. * merged by index. If neither are arrays, we use normal merge.
  42122. * @private
  42123. */
  42124. function mergeArrays(one, two) {
  42125. let res = [], i;
  42126. if (isArray(one) && !isArray(two)) {
  42127. res = one.map(function (el) {
  42128. return merge(el, two);
  42129. });
  42130. }
  42131. else if (isArray(two) && !isArray(one)) {
  42132. res = two.map(function (el) {
  42133. return merge(one, el);
  42134. });
  42135. }
  42136. else if (!isArray(one) && !isArray(two)) {
  42137. res = merge(one, two);
  42138. }
  42139. else if (isArray(one) && isArray(two)) {
  42140. i = Math.max(one.length, two.length);
  42141. while (i--) {
  42142. res[i] = merge(one[i], two[i]);
  42143. }
  42144. }
  42145. return res;
  42146. }
  42147. /**
  42148. * Set starting position for data label sorting animation.
  42149. * @private
  42150. */
  42151. function setDataLabelStartPos(point, dataLabel, isNew, isInside, alignOptions) {
  42152. const chart = this.chart, inverted = chart.inverted, xAxis = this.xAxis, reversed = xAxis.reversed, labelCenter = inverted ? dataLabel.height / 2 : dataLabel.width / 2, pointWidth = point.pointWidth, halfWidth = pointWidth ? pointWidth / 2 : 0;
  42153. dataLabel.startXPos = inverted ?
  42154. alignOptions.x :
  42155. (reversed ?
  42156. -labelCenter - halfWidth :
  42157. xAxis.width - labelCenter + halfWidth);
  42158. dataLabel.startYPos = inverted ?
  42159. (reversed ?
  42160. this.yAxis.height - labelCenter + halfWidth :
  42161. -labelCenter - halfWidth) : alignOptions.y;
  42162. // We need to handle visibility in case of sorting point outside plot
  42163. // area
  42164. if (!isInside) {
  42165. dataLabel
  42166. .attr({ opacity: 1 })
  42167. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  42168. }
  42169. else if (dataLabel.visibility === 'hidden') {
  42170. dataLabel.show();
  42171. dataLabel
  42172. .attr({ opacity: 0 })
  42173. .animate({ opacity: 1 });
  42174. }
  42175. // Save start position on first render, but do not change position
  42176. if (!chart.hasRendered) {
  42177. return;
  42178. }
  42179. // Set start position
  42180. if (isNew) {
  42181. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  42182. }
  42183. dataLabel.placed = true;
  42184. }
  42185. })(DataLabel || (DataLabel = {}));
  42186. /* *
  42187. *
  42188. * Default Export
  42189. *
  42190. * */
  42191. /* *
  42192. *
  42193. * API Declarations
  42194. *
  42195. * */
  42196. /**
  42197. * Callback JavaScript function to format the data label as a string. Note that
  42198. * if a `format` is defined, the format takes precedence and the formatter is
  42199. * ignored.
  42200. *
  42201. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  42202. *
  42203. * @param {Highcharts.PointLabelObject} this
  42204. * Data label context to format
  42205. *
  42206. * @param {Highcharts.DataLabelsOptions} options
  42207. * [API options](/highcharts/plotOptions.series.dataLabels) of the data label
  42208. *
  42209. * @return {number|string|null|undefined}
  42210. * Formatted data label text
  42211. */
  42212. /**
  42213. * Values for handling data labels that flow outside the plot area.
  42214. *
  42215. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  42216. */
  42217. ''; // keeps doclets above in JS file
  42218. return DataLabel;
  42219. });
  42220. _registerModule(_modules, 'Series/Column/ColumnDataLabel.js', [_modules['Core/Series/DataLabel.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DataLabel, SeriesRegistry, U) {
  42221. /* *
  42222. *
  42223. * (c) 2010-2021 Torstein Honsi
  42224. *
  42225. * License: www.highcharts.com/license
  42226. *
  42227. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42228. *
  42229. * */
  42230. const { series: Series } = SeriesRegistry;
  42231. const { merge, pick } = U;
  42232. /* *
  42233. *
  42234. * Composition
  42235. *
  42236. * */
  42237. var ColumnDataLabel;
  42238. (function (ColumnDataLabel) {
  42239. /* *
  42240. *
  42241. * Constants
  42242. *
  42243. * */
  42244. const composedMembers = [];
  42245. /* *
  42246. *
  42247. * Functions
  42248. *
  42249. * */
  42250. /* eslint-disable valid-jsdoc */
  42251. /**
  42252. * Override the basic data label alignment by adjusting for the position of
  42253. * the column.
  42254. * @private
  42255. */
  42256. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  42257. let inverted = this.chart.inverted, series = point.series, xLen = (series.xAxis ? series.xAxis.len : this.chart.plotSizeX) || 0, yLen = (series.yAxis ? series.yAxis.len : this.chart.plotSizeY) || 0,
  42258. // data label box for alignment
  42259. dlBox = point.dlBox || point.shapeArgs, below = pick(point.below, // range series
  42260. point.plotY >
  42261. pick(this.translatedThreshold, yLen)),
  42262. // draw it inside the box?
  42263. inside = pick(options.inside, !!this.options.stacking), overshoot;
  42264. // Align to the column itself, or the top of it
  42265. if (dlBox) { // Area range uses this method but not alignTo
  42266. alignTo = merge(dlBox);
  42267. if (alignTo.y < 0) {
  42268. alignTo.height += alignTo.y;
  42269. alignTo.y = 0;
  42270. }
  42271. // If parts of the box overshoots outside the plot area, modify the
  42272. // box to center the label inside
  42273. overshoot = alignTo.y + alignTo.height - yLen;
  42274. if (overshoot > 0 && overshoot < alignTo.height) {
  42275. alignTo.height -= overshoot;
  42276. }
  42277. if (inverted) {
  42278. alignTo = {
  42279. x: yLen - alignTo.y - alignTo.height,
  42280. y: xLen - alignTo.x - alignTo.width,
  42281. width: alignTo.height,
  42282. height: alignTo.width
  42283. };
  42284. }
  42285. // Compute the alignment box
  42286. if (!inside) {
  42287. if (inverted) {
  42288. alignTo.x += below ? 0 : alignTo.width;
  42289. alignTo.width = 0;
  42290. }
  42291. else {
  42292. alignTo.y += below ? alignTo.height : 0;
  42293. alignTo.height = 0;
  42294. }
  42295. }
  42296. }
  42297. // When alignment is undefined (typically columns and bars), display the
  42298. // individual point below or above the point depending on the threshold
  42299. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  42300. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  42301. // Call the parent method
  42302. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  42303. // If label was justified and we have contrast, set it:
  42304. if (options.inside && point.contrastColor) {
  42305. dataLabel.css({
  42306. color: point.contrastColor
  42307. });
  42308. }
  42309. }
  42310. /** @private */
  42311. function compose(ColumnSeriesClass) {
  42312. DataLabel.compose(Series);
  42313. if (U.pushUnique(composedMembers, ColumnSeriesClass)) {
  42314. ColumnSeriesClass.prototype.alignDataLabel = alignDataLabel;
  42315. }
  42316. }
  42317. ColumnDataLabel.compose = compose;
  42318. })(ColumnDataLabel || (ColumnDataLabel = {}));
  42319. /* *
  42320. *
  42321. * Default Export
  42322. *
  42323. * */
  42324. return ColumnDataLabel;
  42325. });
  42326. _registerModule(_modules, 'Series/Bar/BarSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ColumnSeries, SeriesRegistry, U) {
  42327. /* *
  42328. *
  42329. * (c) 2010-2021 Torstein Honsi
  42330. *
  42331. * License: www.highcharts.com/license
  42332. *
  42333. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42334. *
  42335. * */
  42336. const { extend, merge } = U;
  42337. /* *
  42338. *
  42339. * Class
  42340. *
  42341. * */
  42342. /**
  42343. * Bar series type.
  42344. *
  42345. * @private
  42346. * @class
  42347. * @name Highcharts.seriesTypes.bar
  42348. *
  42349. * @augments Highcharts.Series
  42350. */
  42351. class BarSeries extends ColumnSeries {
  42352. constructor() {
  42353. /* *
  42354. *
  42355. * Static Properties
  42356. *
  42357. * */
  42358. super(...arguments);
  42359. /* *
  42360. *
  42361. * Properties
  42362. *
  42363. * */
  42364. this.data = void 0;
  42365. this.options = void 0;
  42366. this.points = void 0;
  42367. }
  42368. }
  42369. /**
  42370. * A bar series is a special type of column series where the columns are
  42371. * horizontal.
  42372. *
  42373. * @sample highcharts/demo/bar-basic/
  42374. * Bar chart
  42375. *
  42376. * @extends plotOptions.column
  42377. * @product highcharts
  42378. * @optionparent plotOptions.bar
  42379. */
  42380. BarSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  42381. // nothing here yet
  42382. });
  42383. extend(BarSeries.prototype, {
  42384. inverted: true
  42385. });
  42386. SeriesRegistry.registerSeriesType('bar', BarSeries);
  42387. /* *
  42388. *
  42389. * Default Export
  42390. *
  42391. * */
  42392. /* *
  42393. *
  42394. * API Options
  42395. *
  42396. * */
  42397. /**
  42398. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  42399. * it is inherited from [chart.type](#chart.type).
  42400. *
  42401. * @extends series,plotOptions.bar
  42402. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  42403. * linecap, lineWidth, marker, connectEnds, step
  42404. * @product highcharts
  42405. * @apioption series.bar
  42406. */
  42407. /**
  42408. * An array of data points for the series. For the `bar` series type,
  42409. * points can be given in the following ways:
  42410. *
  42411. * 1. An array of numerical values. In this case, the numerical values will be
  42412. * interpreted as `y` options. The `x` values will be automatically
  42413. * calculated, either starting at 0 and incremented by 1, or from
  42414. * `pointStart` and `pointInterval` given in the series options. If the axis
  42415. * has categories, these will be used. Example:
  42416. * ```js
  42417. * data: [0, 5, 3, 5]
  42418. * ```
  42419. *
  42420. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42421. * `x,y`. If the first value is a string, it is applied as the name of the
  42422. * point, and the `x` value is inferred.
  42423. * ```js
  42424. * data: [
  42425. * [0, 5],
  42426. * [1, 10],
  42427. * [2, 3]
  42428. * ]
  42429. * ```
  42430. *
  42431. * 3. An array of objects with named values. The following snippet shows only a
  42432. * few settings, see the complete options set below. If the total number of
  42433. * data points exceeds the series'
  42434. * [turboThreshold](#series.bar.turboThreshold), this option is not
  42435. * available.
  42436. * ```js
  42437. * data: [{
  42438. * x: 1,
  42439. * y: 1,
  42440. * name: "Point2",
  42441. * color: "#00FF00"
  42442. * }, {
  42443. * x: 1,
  42444. * y: 10,
  42445. * name: "Point1",
  42446. * color: "#FF00FF"
  42447. * }]
  42448. * ```
  42449. *
  42450. * @sample {highcharts} highcharts/chart/reflow-true/
  42451. * Numerical values
  42452. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42453. * Arrays of numeric x and y
  42454. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42455. * Arrays of datetime x and y
  42456. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42457. * Arrays of point.name and y
  42458. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42459. * Config objects
  42460. *
  42461. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42462. * @extends series.column.data
  42463. * @product highcharts
  42464. * @apioption series.bar.data
  42465. */
  42466. /**
  42467. * @excluding halo,lineWidth,lineWidthPlus,marker
  42468. * @product highcharts highstock
  42469. * @apioption series.bar.states.hover
  42470. */
  42471. /**
  42472. * @excluding halo,lineWidth,lineWidthPlus,marker
  42473. * @product highcharts highstock
  42474. * @apioption series.bar.states.select
  42475. */
  42476. ''; // gets doclets above into transpilat
  42477. return BarSeries;
  42478. });
  42479. _registerModule(_modules, 'Series/Scatter/ScatterSeriesDefaults.js', [], function () {
  42480. /* *
  42481. *
  42482. * (c) 2010-2021 Torstein Honsi
  42483. *
  42484. * License: www.highcharts.com/license
  42485. *
  42486. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42487. *
  42488. * */
  42489. /* *
  42490. *
  42491. * API Options
  42492. *
  42493. * */
  42494. /**
  42495. * A scatter plot uses cartesian coordinates to display values for two
  42496. * variables for a set of data.
  42497. *
  42498. * @sample {highcharts} highcharts/demo/scatter/
  42499. * Scatter plot
  42500. *
  42501. * @extends plotOptions.line
  42502. * @excluding cropThreshold, pointPlacement, shadow, useOhlcData
  42503. * @product highcharts highstock
  42504. * @optionparent plotOptions.scatter
  42505. */
  42506. const ScatterSeriesDefaults = {
  42507. /**
  42508. * The width of the line connecting the data points.
  42509. *
  42510. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  42511. * 0 by default
  42512. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  42513. * 1px
  42514. *
  42515. * @product highcharts highstock
  42516. */
  42517. lineWidth: 0,
  42518. findNearestPointBy: 'xy',
  42519. /**
  42520. * Apply a jitter effect for the rendered markers. When plotting
  42521. * discrete values, a little random noise may help telling the points
  42522. * apart. The jitter setting applies a random displacement of up to `n`
  42523. * axis units in either direction. So for example on a horizontal X
  42524. * axis, setting the `jitter.x` to 0.24 will render the point in a
  42525. * random position between 0.24 units to the left and 0.24 units to the
  42526. * right of the true axis position. On a category axis, setting it to
  42527. * 0.5 will fill up the bin and make the data appear continuous.
  42528. *
  42529. * When rendered on top of a box plot or a column series, a jitter value
  42530. * of 0.24 will correspond to the underlying series' default
  42531. * [groupPadding](
  42532. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  42533. * and [pointPadding](
  42534. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  42535. * settings.
  42536. *
  42537. * @sample {highcharts} highcharts/demo/scatter-jitter
  42538. * Jitter on a scatter plot
  42539. *
  42540. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  42541. * Jittered scatter plot on top of a box plot
  42542. *
  42543. * @product highcharts highstock
  42544. * @since 7.0.2
  42545. */
  42546. jitter: {
  42547. /**
  42548. * The maximal X offset for the random jitter effect.
  42549. */
  42550. x: 0,
  42551. /**
  42552. * The maximal Y offset for the random jitter effect.
  42553. */
  42554. y: 0
  42555. },
  42556. marker: {
  42557. enabled: true // Overrides auto-enabling in line series (#3647)
  42558. },
  42559. /**
  42560. * Sticky tracking of mouse events. When true, the `mouseOut` event
  42561. * on a series isn't triggered until the mouse moves over another
  42562. * series, or out of the plot area. When false, the `mouseOut` event on
  42563. * a series is triggered when the mouse leaves the area around the
  42564. * series' graph or markers. This also implies the tooltip. When
  42565. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  42566. * will be hidden when moving the mouse between series.
  42567. *
  42568. * @type {boolean}
  42569. * @default false
  42570. * @product highcharts highstock highmaps
  42571. * @apioption plotOptions.scatter.stickyTracking
  42572. */
  42573. /**
  42574. * A configuration object for the tooltip rendering of each single
  42575. * series. Properties are inherited from [tooltip](#tooltip).
  42576. * Overridable properties are `headerFormat`, `pointFormat`,
  42577. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  42578. * series, in a scatter plot the series.name by default shows in the
  42579. * headerFormat and point.x and point.y in the pointFormat.
  42580. *
  42581. * @product highcharts highstock highmaps
  42582. */
  42583. tooltip: {
  42584. /**
  42585. * @product highcharts highstock
  42586. */
  42587. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  42588. '<span style="font-size: 0.8em"> {series.name}</span><br/>',
  42589. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  42590. }
  42591. };
  42592. /**
  42593. * A `scatter` series. If the [type](#series.scatter.type) option is
  42594. * not specified, it is inherited from [chart.type](#chart.type).
  42595. *
  42596. * @extends series,plotOptions.scatter
  42597. * @excluding cropThreshold, dataParser, dataURL, useOhlcData
  42598. * @product highcharts highstock
  42599. * @apioption series.scatter
  42600. */
  42601. /**
  42602. * An array of data points for the series. For the `scatter` series
  42603. * type, points can be given in the following ways:
  42604. *
  42605. * 1. An array of numerical values. In this case, the numerical values will be
  42606. * interpreted as `y` options. The `x` values will be automatically
  42607. * calculated, either starting at 0 and incremented by 1, or from
  42608. * `pointStart` and `pointInterval` given in the series options. If the axis
  42609. * has categories, these will be used. Example:
  42610. * ```js
  42611. * data: [0, 5, 3, 5]
  42612. * ```
  42613. *
  42614. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42615. * `x,y`. If the first value is a string, it is applied as the name of the
  42616. * point, and the `x` value is inferred.
  42617. * ```js
  42618. * data: [
  42619. * [0, 0],
  42620. * [1, 8],
  42621. * [2, 9]
  42622. * ]
  42623. * ```
  42624. *
  42625. * 3. An array of objects with named values. The following snippet shows only a
  42626. * few settings, see the complete options set below. If the total number of
  42627. * data points exceeds the series'
  42628. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  42629. * available.
  42630. * ```js
  42631. * data: [{
  42632. * x: 1,
  42633. * y: 2,
  42634. * name: "Point2",
  42635. * color: "#00FF00"
  42636. * }, {
  42637. * x: 1,
  42638. * y: 4,
  42639. * name: "Point1",
  42640. * color: "#FF00FF"
  42641. * }]
  42642. * ```
  42643. *
  42644. * @sample {highcharts} highcharts/chart/reflow-true/
  42645. * Numerical values
  42646. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42647. * Arrays of numeric x and y
  42648. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42649. * Arrays of datetime x and y
  42650. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42651. * Arrays of point.name and y
  42652. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42653. * Config objects
  42654. *
  42655. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42656. * @extends series.line.data
  42657. * @product highcharts highstock
  42658. * @apioption series.scatter.data
  42659. */
  42660. ''; // keeps doclets above in JS file
  42661. /* *
  42662. *
  42663. * Default Export
  42664. *
  42665. * */
  42666. return ScatterSeriesDefaults;
  42667. });
  42668. _registerModule(_modules, 'Series/Scatter/ScatterSeries.js', [_modules['Series/Scatter/ScatterSeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ScatterSeriesDefaults, SeriesRegistry, U) {
  42669. /* *
  42670. *
  42671. * (c) 2010-2021 Torstein Honsi
  42672. *
  42673. * License: www.highcharts.com/license
  42674. *
  42675. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42676. *
  42677. * */
  42678. const { column: ColumnSeries, line: LineSeries } = SeriesRegistry.seriesTypes;
  42679. const { addEvent, extend, merge } = U;
  42680. /* *
  42681. *
  42682. * Class
  42683. *
  42684. * */
  42685. /**
  42686. * Scatter series type.
  42687. *
  42688. * @private
  42689. */
  42690. class ScatterSeries extends LineSeries {
  42691. constructor() {
  42692. /* *
  42693. *
  42694. * Static Properties
  42695. *
  42696. * */
  42697. super(...arguments);
  42698. /* *
  42699. *
  42700. * Properties
  42701. *
  42702. * */
  42703. this.data = void 0;
  42704. this.options = void 0;
  42705. this.points = void 0;
  42706. /* eslint-enable valid-jsdoc */
  42707. }
  42708. /* *
  42709. *
  42710. * Functions
  42711. *
  42712. * */
  42713. /* eslint-disable valid-jsdoc */
  42714. /**
  42715. * Optionally add the jitter effect.
  42716. * @private
  42717. */
  42718. applyJitter() {
  42719. const series = this, jitter = this.options.jitter, len = this.points.length;
  42720. /**
  42721. * Return a repeatable, pseudo-random number based on an integer
  42722. * seed.
  42723. * @private
  42724. */
  42725. function unrandom(seed) {
  42726. const rand = Math.sin(seed) * 10000;
  42727. return rand - Math.floor(rand);
  42728. }
  42729. if (jitter) {
  42730. this.points.forEach(function (point, i) {
  42731. ['x', 'y'].forEach(function (dim, j) {
  42732. let axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
  42733. if (jitter[dim] && !point.isNull) {
  42734. axis = series[dim + 'Axis'];
  42735. translatedJitter =
  42736. jitter[dim] * axis.transA;
  42737. if (axis && !axis.isLog) {
  42738. // Identify the outer bounds of the jitter range
  42739. min = Math.max(0, point[plotProp] - translatedJitter);
  42740. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  42741. // Find a random position within this range
  42742. point[plotProp] = min +
  42743. (max - min) * unrandom(i + j * len);
  42744. // Update clientX for the tooltip k-d-tree
  42745. if (dim === 'x') {
  42746. point.clientX = point.plotX;
  42747. }
  42748. }
  42749. }
  42750. });
  42751. });
  42752. }
  42753. }
  42754. /**
  42755. * @private
  42756. */
  42757. drawGraph() {
  42758. if (this.options.lineWidth) {
  42759. super.drawGraph();
  42760. }
  42761. else if (this.graph) {
  42762. this.graph = this.graph.destroy();
  42763. }
  42764. }
  42765. }
  42766. ScatterSeries.defaultOptions = merge(LineSeries.defaultOptions, ScatterSeriesDefaults);
  42767. extend(ScatterSeries.prototype, {
  42768. drawTracker: ColumnSeries.prototype.drawTracker,
  42769. sorted: false,
  42770. requireSorting: false,
  42771. noSharedTooltip: true,
  42772. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  42773. takeOrdinalPosition: false // #2342
  42774. });
  42775. /* *
  42776. *
  42777. * Events
  42778. *
  42779. * */
  42780. /* eslint-disable no-invalid-this */
  42781. addEvent(ScatterSeries, 'afterTranslate', function () {
  42782. this.applyJitter();
  42783. });
  42784. SeriesRegistry.registerSeriesType('scatter', ScatterSeries);
  42785. /* *
  42786. *
  42787. * Default Export
  42788. *
  42789. * */
  42790. return ScatterSeries;
  42791. });
  42792. _registerModule(_modules, 'Series/CenteredUtilities.js', [_modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (H, Series, U) {
  42793. /* *
  42794. *
  42795. * (c) 2010-2021 Torstein Honsi
  42796. *
  42797. * License: www.highcharts.com/license
  42798. *
  42799. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42800. *
  42801. * */
  42802. const { deg2rad } = H;
  42803. const { fireEvent, isNumber, pick, relativeLength } = U;
  42804. /**
  42805. * @private
  42806. */
  42807. var CenteredUtilities;
  42808. (function (CenteredUtilities) {
  42809. /* *
  42810. *
  42811. * Declarations
  42812. *
  42813. * */
  42814. /* *
  42815. *
  42816. * Functions
  42817. *
  42818. * */
  42819. /* eslint-disable valid-jsdoc */
  42820. /**
  42821. * Get the center of the pie based on the size and center options relative
  42822. * to the plot area. Borrowed by the polar and gauge series types.
  42823. *
  42824. * @private
  42825. * @function Highcharts.CenteredSeriesMixin.getCenter
  42826. */
  42827. function getCenter() {
  42828. const options = this.options, chart = this.chart, slicingRoom = 2 * (options.slicedOffset || 0), plotWidth = chart.plotWidth - 2 * slicingRoom, plotHeight = chart.plotHeight - 2 * slicingRoom, centerOption = options.center, smallestSize = Math.min(plotWidth, plotHeight), thickness = options.thickness;
  42829. let handleSlicingRoom, size = options.size, innerSize = options.innerSize || 0, i, value;
  42830. if (typeof size === 'string') {
  42831. size = parseFloat(size);
  42832. }
  42833. if (typeof innerSize === 'string') {
  42834. innerSize = parseFloat(innerSize);
  42835. }
  42836. const positions = [
  42837. pick(centerOption[0], '50%'),
  42838. pick(centerOption[1], '50%'),
  42839. // Prevent from negative values
  42840. pick(size && size < 0 ? void 0 : options.size, '100%'),
  42841. pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
  42842. ];
  42843. // No need for inner size in angular (gauges) series but still required
  42844. // for pie series
  42845. if (chart.angular && !(this instanceof Series)) {
  42846. positions[3] = 0;
  42847. }
  42848. for (i = 0; i < 4; ++i) {
  42849. value = positions[i];
  42850. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  42851. // i == 0: centerX, relative to width
  42852. // i == 1: centerY, relative to height
  42853. // i == 2: size, relative to smallestSize
  42854. // i == 3: innerSize, relative to size
  42855. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  42856. }
  42857. // innerSize cannot be larger than size (#3632)
  42858. if (positions[3] > positions[2]) {
  42859. positions[3] = positions[2];
  42860. }
  42861. // thickness overrides innerSize, need to be less than pie size (#6647)
  42862. if (isNumber(thickness) &&
  42863. thickness * 2 < positions[2] && thickness > 0) {
  42864. positions[3] = positions[2] - thickness * 2;
  42865. }
  42866. fireEvent(this, 'afterGetCenter', { positions });
  42867. return positions;
  42868. }
  42869. CenteredUtilities.getCenter = getCenter;
  42870. /**
  42871. * getStartAndEndRadians - Calculates start and end angles in radians.
  42872. * Used in series types such as pie and sunburst.
  42873. *
  42874. * @private
  42875. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  42876. *
  42877. * @param {number} [start]
  42878. * Start angle in degrees.
  42879. *
  42880. * @param {number} [end]
  42881. * Start angle in degrees.
  42882. *
  42883. * @return {Highcharts.RadianAngles}
  42884. * Returns an object containing start and end angles as radians.
  42885. */
  42886. function getStartAndEndRadians(start, end) {
  42887. const startAngle = isNumber(start) ? start : 0, // must be a number
  42888. endAngle = ((isNumber(end) && // must be a number
  42889. end > startAngle && // must be larger than the start angle
  42890. // difference must be less than 360 degrees
  42891. (end - startAngle) < 360) ?
  42892. end :
  42893. startAngle + 360), correction = -90;
  42894. return {
  42895. start: deg2rad * (startAngle + correction),
  42896. end: deg2rad * (endAngle + correction)
  42897. };
  42898. }
  42899. CenteredUtilities.getStartAndEndRadians = getStartAndEndRadians;
  42900. })(CenteredUtilities || (CenteredUtilities = {}));
  42901. /* *
  42902. *
  42903. * Default Export
  42904. *
  42905. * */
  42906. /* *
  42907. *
  42908. * API Declarations
  42909. *
  42910. * */
  42911. /**
  42912. * @private
  42913. * @interface Highcharts.RadianAngles
  42914. */ /**
  42915. * @name Highcharts.RadianAngles#end
  42916. * @type {number}
  42917. */ /**
  42918. * @name Highcharts.RadianAngles#start
  42919. * @type {number}
  42920. */
  42921. ''; // keeps doclets above in JS file
  42922. return CenteredUtilities;
  42923. });
  42924. _registerModule(_modules, 'Series/Pie/PiePoint.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (A, Point, U) {
  42925. /* *
  42926. *
  42927. * (c) 2010-2021 Torstein Honsi
  42928. *
  42929. * License: www.highcharts.com/license
  42930. *
  42931. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42932. *
  42933. * */
  42934. const { setAnimation } = A;
  42935. const { addEvent, defined, extend, isNumber, pick, relativeLength } = U;
  42936. /* *
  42937. *
  42938. * Class
  42939. *
  42940. * */
  42941. class PiePoint extends Point {
  42942. constructor() {
  42943. /* *
  42944. *
  42945. * Properties
  42946. *
  42947. * */
  42948. super(...arguments);
  42949. this.labelDistance = void 0;
  42950. this.options = void 0;
  42951. this.series = void 0;
  42952. }
  42953. /* *
  42954. *
  42955. * Functions
  42956. *
  42957. * */
  42958. /* eslint-disable valid-jsdoc */
  42959. /**
  42960. * Extendable method for getting the path of the connector between the
  42961. * data label and the pie slice.
  42962. * @private
  42963. */
  42964. getConnectorPath() {
  42965. const labelPosition = this.labelPosition, options = this.series.options.dataLabels, predefinedShapes = this.connectorShapes;
  42966. let connectorShape = options.connectorShape;
  42967. // find out whether to use the predefined shape
  42968. if (predefinedShapes[connectorShape]) {
  42969. connectorShape = predefinedShapes[connectorShape];
  42970. }
  42971. return connectorShape.call(this, {
  42972. // pass simplified label position object for user's convenience
  42973. x: labelPosition.computed.x,
  42974. y: labelPosition.computed.y,
  42975. alignment: labelPosition.alignment
  42976. }, labelPosition.connectorPosition, options);
  42977. }
  42978. /**
  42979. * @private
  42980. */
  42981. getTranslate() {
  42982. return this.sliced ? this.slicedTranslation : {
  42983. translateX: 0,
  42984. translateY: 0
  42985. };
  42986. }
  42987. /**
  42988. * @private
  42989. */
  42990. haloPath(size) {
  42991. const shapeArgs = this.shapeArgs;
  42992. return this.sliced || !this.visible ?
  42993. [] :
  42994. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  42995. // Substract 1px to ensure the background is not bleeding
  42996. // through between the halo and the slice (#7495).
  42997. innerR: shapeArgs.r - 1,
  42998. start: shapeArgs.start,
  42999. end: shapeArgs.end,
  43000. borderRadius: shapeArgs.borderRadius
  43001. });
  43002. }
  43003. /**
  43004. * Initialize the pie slice.
  43005. * @private
  43006. */
  43007. init() {
  43008. super.init.apply(this, arguments);
  43009. this.name = pick(this.name, 'Slice');
  43010. // add event listener for select
  43011. const toggleSlice = (e) => {
  43012. this.slice(e.type === 'select');
  43013. };
  43014. addEvent(this, 'select', toggleSlice);
  43015. addEvent(this, 'unselect', toggleSlice);
  43016. return this;
  43017. }
  43018. /**
  43019. * Negative points are not valid (#1530, #3623, #5322)
  43020. * @private
  43021. */
  43022. isValid() {
  43023. return isNumber(this.y) && this.y >= 0;
  43024. }
  43025. /**
  43026. * Toggle the visibility of a pie slice or other data point. Note that this
  43027. * method is available only for some series, like pie, treemap and sunburst.
  43028. *
  43029. * @function Highcharts.Point#setVisible
  43030. *
  43031. * @param {boolean} [vis]
  43032. * True to show the pie slice or other data point, false to hide. If
  43033. * undefined, the visibility is toggled.
  43034. *
  43035. * @param {boolean} [redraw] Whether to redraw the chart after the point is
  43036. * altered. If doing more operations on the chart, it is a good idea to set
  43037. * redraw to false and call {@link Chart#redraw|chart.redraw()} after.
  43038. *
  43039. */
  43040. setVisible(vis, redraw) {
  43041. const series = this.series, chart = series.chart, ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  43042. redraw = pick(redraw, ignoreHiddenPoint);
  43043. if (vis !== this.visible) {
  43044. // If called without an argument, toggle visibility
  43045. this.visible = this.options.visible = vis =
  43046. typeof vis === 'undefined' ? !this.visible : vis;
  43047. // update userOptions.data
  43048. series.options.data[series.data.indexOf(this)] =
  43049. this.options;
  43050. // Show and hide associated elements. This is performed
  43051. // regardless of redraw or not, because chart.redraw only
  43052. // handles full series.
  43053. ['graphic', 'dataLabel', 'connector'].forEach((key) => {
  43054. if (this[key]) {
  43055. this[key][vis ? 'show' : 'hide'](vis);
  43056. }
  43057. });
  43058. if (this.legendItem) {
  43059. chart.legend.colorizeItem(this, vis);
  43060. }
  43061. // #4170, hide halo after hiding point
  43062. if (!vis && this.state === 'hover') {
  43063. this.setState('');
  43064. }
  43065. // Handle ignore hidden slices
  43066. if (ignoreHiddenPoint) {
  43067. series.isDirty = true;
  43068. }
  43069. if (redraw) {
  43070. chart.redraw();
  43071. }
  43072. }
  43073. }
  43074. /**
  43075. * Set or toggle whether the slice is cut out from the pie.
  43076. * @private
  43077. *
  43078. * @param {boolean} sliced
  43079. * When undefined, the slice state is toggled.
  43080. *
  43081. * @param {boolean} [redraw]
  43082. * Whether to redraw the chart. True by default.
  43083. *
  43084. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  43085. * Animation options.
  43086. */
  43087. slice(sliced, redraw, animation) {
  43088. const series = this.series, chart = series.chart;
  43089. setAnimation(animation, chart);
  43090. // redraw is true by default
  43091. redraw = pick(redraw, true);
  43092. /**
  43093. * Pie series only. Whether to display a slice offset from the
  43094. * center.
  43095. * @name Highcharts.Point#sliced
  43096. * @type {boolean|undefined}
  43097. */
  43098. // if called without an argument, toggle
  43099. this.sliced = this.options.sliced = sliced =
  43100. defined(sliced) ? sliced : !this.sliced;
  43101. // update userOptions.data
  43102. series.options.data[series.data.indexOf(this)] =
  43103. this.options;
  43104. if (this.graphic) {
  43105. this.graphic.animate(this.getTranslate());
  43106. }
  43107. }
  43108. }
  43109. extend(PiePoint.prototype, {
  43110. connectorShapes: {
  43111. // only one available before v7.0.0
  43112. fixedOffset: function (labelPosition, connectorPosition, options) {
  43113. const breakAt = connectorPosition.breakAt, touchingSliceAt = connectorPosition.touchingSliceAt, lineSegment = options.softConnector ? [
  43114. 'C',
  43115. // 1st control point (of the curve)
  43116. labelPosition.x +
  43117. // 5 gives the connector a little horizontal bend
  43118. (labelPosition.alignment === 'left' ? -5 : 5),
  43119. labelPosition.y,
  43120. 2 * breakAt.x - touchingSliceAt.x,
  43121. 2 * breakAt.y - touchingSliceAt.y,
  43122. breakAt.x,
  43123. breakAt.y //
  43124. ] : [
  43125. 'L',
  43126. breakAt.x,
  43127. breakAt.y
  43128. ];
  43129. // assemble the path
  43130. return ([
  43131. ['M', labelPosition.x, labelPosition.y],
  43132. lineSegment,
  43133. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43134. ]);
  43135. },
  43136. straight: function (labelPosition, connectorPosition) {
  43137. const touchingSliceAt = connectorPosition.touchingSliceAt;
  43138. // direct line to the slice
  43139. return [
  43140. ['M', labelPosition.x, labelPosition.y],
  43141. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43142. ];
  43143. },
  43144. crookedLine: function (labelPosition, connectorPosition, options) {
  43145. const { breakAt, touchingSliceAt } = connectorPosition, { series } = this, [cx, cy, diameter] = series.center, r = diameter / 2, plotWidth = series.chart.plotWidth, plotLeft = series.chart.plotLeft, leftAligned = labelPosition.alignment === 'left', { x, y } = labelPosition;
  43146. let crookX = breakAt.x;
  43147. if (options.crookDistance) {
  43148. const crookDistance = relativeLength(// % to fraction
  43149. options.crookDistance, 1);
  43150. crookX = leftAligned ?
  43151. cx +
  43152. r +
  43153. (plotWidth + plotLeft - cx - r) * (1 - crookDistance) :
  43154. plotLeft + (cx - r) * crookDistance;
  43155. // When the crookDistance option is undefined, make the bend in the
  43156. // intersection between the radial line in the middle of the slice,
  43157. // and the extension of the label position.
  43158. }
  43159. else {
  43160. crookX = cx + (cy - y) * Math.tan((this.angle || 0) - Math.PI / 2);
  43161. }
  43162. const path = [['M', x, y]];
  43163. // The crookedLine formula doesn't make sense if the path overlaps
  43164. // the label - use straight line instead in that case
  43165. if (leftAligned ?
  43166. (crookX <= x && crookX >= breakAt.x) :
  43167. (crookX >= x && crookX <= breakAt.x)) {
  43168. path.push(['L', crookX, y]);
  43169. }
  43170. path.push(['L', breakAt.x, breakAt.y], ['L', touchingSliceAt.x, touchingSliceAt.y]);
  43171. return path;
  43172. }
  43173. }
  43174. });
  43175. /* *
  43176. *
  43177. * Default Export
  43178. *
  43179. * */
  43180. return PiePoint;
  43181. });
  43182. _registerModule(_modules, 'Series/Pie/PieSeriesDefaults.js', [], function () {
  43183. /* *
  43184. *
  43185. * (c) 2010-2021 Torstein Honsi
  43186. *
  43187. * License: www.highcharts.com/license
  43188. *
  43189. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43190. *
  43191. * */
  43192. /* *
  43193. *
  43194. * API Options
  43195. *
  43196. * */
  43197. /**
  43198. * A pie chart is a circular graphic which is divided into slices to
  43199. * illustrate numerical proportion.
  43200. *
  43201. * @sample highcharts/demo/pie-basic/
  43202. * Pie chart
  43203. *
  43204. * @extends plotOptions.line
  43205. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  43206. * cropThreshold, dashStyle, dataSorting, dragDrop,
  43207. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  43208. * linkedTo, marker, negativeColor, pointInterval,
  43209. * pointIntervalUnit, pointPlacement, pointStart,
  43210. * softThreshold, stacking, step, threshold, turboThreshold,
  43211. * zoneAxis, zones, dataSorting, boostBlending
  43212. * @product highcharts highmaps
  43213. * @optionparent plotOptions.pie
  43214. *
  43215. * @private
  43216. */
  43217. const PieSeriesDefaults = {
  43218. /**
  43219. * The corner radius of the border surrounding each slice. A number
  43220. * signifies pixels. A percentage string, like for example `50%`, signifies
  43221. * a size relative to the radius and the inner radius.
  43222. *
  43223. * @sample highcharts/plotoptions/series-border-radius
  43224. * Column and pie with rounded border
  43225. *
  43226. * @since 11.0.0
  43227. *
  43228. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  43229. */
  43230. borderRadius: 3,
  43231. /**
  43232. * @excluding legendItemClick
  43233. * @apioption plotOptions.pie.events
  43234. */
  43235. /**
  43236. * Fires when the checkbox next to the point name in the legend is
  43237. * clicked. One parameter, event, is passed to the function. The state
  43238. * of the checkbox is found by event.checked. The checked item is found
  43239. * by event.item. Return false to prevent the default action which is to
  43240. * toggle the select state of the series.
  43241. *
  43242. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  43243. * Alert checkbox status
  43244. *
  43245. * @type {Function}
  43246. * @since 1.2.0
  43247. * @product highcharts highmaps
  43248. * @context Highcharts.Point
  43249. * @apioption plotOptions.pie.events.checkboxClick
  43250. */
  43251. /**
  43252. * Fires when the legend item belonging to the pie point (slice) is
  43253. * clicked. The `this` keyword refers to the point itself. One
  43254. * parameter, `event`, is passed to the function, containing common
  43255. * event information. The default action is to toggle the visibility of
  43256. * the point. This can be prevented by calling `event.preventDefault()`.
  43257. *
  43258. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  43259. * Confirm toggle visibility
  43260. *
  43261. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  43262. * @since 1.2.0
  43263. * @product highcharts highmaps
  43264. * @apioption plotOptions.pie.point.events.legendItemClick
  43265. */
  43266. /**
  43267. * The center of the pie chart relative to the plot area. Can be
  43268. * percentages or pixel values. The default behaviour (as of 3.0) is to
  43269. * center the pie so that all slices and data labels are within the plot
  43270. * area. As a consequence, the pie may actually jump around in a chart
  43271. * with dynamic values, as the data labels move. In that case, the
  43272. * center should be explicitly set, for example to `["50%", "50%"]`.
  43273. *
  43274. * @sample {highcharts} highcharts/plotoptions/pie-center/
  43275. * Centered at 100, 100
  43276. *
  43277. * @type {Array<(number|string|null),(number|string|null)>}
  43278. * @default [null, null]
  43279. * @product highcharts highmaps
  43280. *
  43281. * @private
  43282. */
  43283. center: [null, null],
  43284. /**
  43285. * The color of the pie series. A pie series is represented as an empty
  43286. * circle if the total sum of its values is 0. Use this property to
  43287. * define the color of its border.
  43288. *
  43289. * In styled mode, the color can be defined by the
  43290. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  43291. * color can be set with the `.highcharts-series`,
  43292. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  43293. * `.highcharts-series-{n}` class, or individual classes given by the
  43294. * `className` option.
  43295. *
  43296. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43297. * Empty pie series
  43298. *
  43299. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43300. * @default #cccccc
  43301. * @apioption plotOptions.pie.color
  43302. */
  43303. /**
  43304. * @product highcharts
  43305. *
  43306. * @private
  43307. */
  43308. clip: false,
  43309. /**
  43310. * @ignore-option
  43311. *
  43312. * @private
  43313. */
  43314. colorByPoint: true,
  43315. /**
  43316. * A series specific or series type specific color set to use instead
  43317. * of the global [colors](#colors).
  43318. *
  43319. * @sample {highcharts} highcharts/demo/pie-monochrome/
  43320. * Set default colors for all pies
  43321. *
  43322. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  43323. * @since 3.0
  43324. * @product highcharts highmaps
  43325. * @apioption plotOptions.pie.colors
  43326. */
  43327. /**
  43328. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  43329. * @extends plotOptions.series.dataLabels
  43330. * @excluding align, allowOverlap, inside, staggerLines, step
  43331. * @private
  43332. */
  43333. dataLabels: {
  43334. /**
  43335. * Alignment method for data labels. Possible values are:
  43336. *
  43337. * - `plotEdges`: Each label touches the nearest vertical edge of
  43338. * the plot area.
  43339. *
  43340. * - `connectors`: Connectors have the same x position and the
  43341. * widest label of each half (left & right) touches the nearest
  43342. * vertical edge of the plot area.
  43343. *
  43344. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  43345. * alignTo: connectors
  43346. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  43347. * alignTo: plotEdges
  43348. *
  43349. * @type {string}
  43350. * @since 7.0.0
  43351. * @product highcharts highmaps
  43352. * @apioption plotOptions.pie.dataLabels.alignTo
  43353. */
  43354. allowOverlap: true,
  43355. /**
  43356. * The color of the line connecting the data label to the pie slice.
  43357. * The default color is the same as the point's color.
  43358. *
  43359. * In styled mode, the connector stroke is given in the
  43360. * `.highcharts-data-label-connector` class.
  43361. *
  43362. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  43363. * Blue connectors
  43364. * @sample {highcharts} highcharts/css/pie-point/
  43365. * Styled connectors
  43366. *
  43367. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43368. * @since 2.1
  43369. * @product highcharts highmaps
  43370. * @apioption plotOptions.pie.dataLabels.connectorColor
  43371. */
  43372. /**
  43373. * The distance from the data label to the connector. Note that
  43374. * data labels also have a default `padding`, so in order for the
  43375. * connector to touch the text, the `padding` must also be 0.
  43376. *
  43377. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  43378. * No padding
  43379. *
  43380. * @since 2.1
  43381. * @product highcharts highmaps
  43382. */
  43383. connectorPadding: 5,
  43384. /**
  43385. * Specifies the method that is used to generate the connector path.
  43386. * Highcharts provides 3 built-in connector shapes: `'crookedLine'`
  43387. * (default since v11), `'fixedOffset'` and `'straight'`.
  43388. *
  43389. * Users can provide their own method by passing a function instead of a
  43390. * string. Three arguments are passed to the callback:
  43391. *
  43392. * - An object that holds the information about the coordinates of the
  43393. * label (`x` & `y` properties) and how the label is located in
  43394. * relation to the pie (`alignment` property). `alignment` can by one
  43395. * of the following: `'left'` (pie on the left side of the data
  43396. * label), `'right'` (pie on the right side of the data label) or
  43397. * `'center'` (data label overlaps the pie).
  43398. *
  43399. * - An object that holds the information about the position of the
  43400. * connector. Its `touchingSliceAt` porperty tells the position of
  43401. * the place where the connector touches the slice.
  43402. *
  43403. * - Data label options
  43404. *
  43405. * The function has to return an SVG path definition in array form (see
  43406. * the example).
  43407. *
  43408. * @sample {highcharts}
  43409. * highcharts/plotoptions/pie-datalabels-connectorshape-string/
  43410. * connectorShape is a String
  43411. * @sample {highcharts}
  43412. * highcharts/plotoptions/pie-datalabels-connectorshape-function/
  43413. * connectorShape is a function
  43414. *
  43415. * @type {string|Function}
  43416. * @since 7.0.0
  43417. * @product highcharts highmaps
  43418. */
  43419. connectorShape: 'crookedLine',
  43420. /**
  43421. * The width of the line connecting the data label to the pie slice.
  43422. *
  43423. * In styled mode, the connector stroke width is given in the
  43424. * `.highcharts-data-label-connector` class.
  43425. *
  43426. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  43427. * Disable the connector
  43428. * @sample {highcharts} highcharts/css/pie-point/
  43429. * Styled connectors
  43430. *
  43431. * @type {number}
  43432. * @default 1
  43433. * @since 2.1
  43434. * @product highcharts highmaps
  43435. * @apioption plotOptions.pie.dataLabels.connectorWidth
  43436. */
  43437. /**
  43438. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  43439. * far from the vertical plot edge the coonnector path should be
  43440. * crooked. With the default, `undefined`, the crook is placed so that
  43441. * the horizontal line from the label intersects with the radial line
  43442. * extending through the center of the pie slice.
  43443. *
  43444. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  43445. * crookDistance set to 90%
  43446. *
  43447. * @since 7.0.0
  43448. * @product highcharts highmaps
  43449. */
  43450. crookDistance: void 0,
  43451. /**
  43452. * The distance of the data label from the pie's edge. Negative
  43453. * numbers put the data label on top of the pie slices. Can also be
  43454. * defined as a percentage of pie's radius. Connectors are only
  43455. * shown for data labels outside the pie.
  43456. *
  43457. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  43458. * Data labels on top of the pie
  43459. *
  43460. * @type {number|string}
  43461. * @since 2.1
  43462. * @product highcharts highmaps
  43463. */
  43464. distance: 30,
  43465. enabled: true,
  43466. /**
  43467. * A
  43468. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  43469. * for the data label. Available variables are the same as for
  43470. * `formatter`.
  43471. *
  43472. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  43473. * Add a unit
  43474. *
  43475. * @type {string}
  43476. * @default undefined
  43477. * @since 3.0
  43478. * @apioption plotOptions.pie.dataLabels.format
  43479. */
  43480. // eslint-disable-next-line valid-jsdoc
  43481. /**
  43482. * Callback JavaScript function to format the data label. Note that
  43483. * if a `format` is defined, the format takes precedence and the
  43484. * formatter is ignored.
  43485. *
  43486. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  43487. * @default function () { return this.point.isNull ? void 0 : this.point.name; }
  43488. */
  43489. formatter: function () {
  43490. return this.point.isNull ? void 0 : this.point.name;
  43491. },
  43492. /**
  43493. * Whether to render the connector as a soft arc or a line with a sharp
  43494. * break. Works only if `connectorShape` equals to `fixedOffset`.
  43495. *
  43496. * @sample {highcharts}
  43497. * highcharts/plotoptions/pie-datalabels-softconnector-true/
  43498. * Soft
  43499. * @sample {highcharts}
  43500. * highcharts/plotoptions/pie-datalabels-softconnector-false/
  43501. * Non soft
  43502. *
  43503. * @since 2.1.7
  43504. * @product highcharts highmaps
  43505. */
  43506. softConnector: true,
  43507. /**
  43508. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
  43509. * Long labels truncated with an ellipsis
  43510. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
  43511. * Long labels are wrapped
  43512. *
  43513. * @type {Highcharts.CSSObject}
  43514. * @apioption plotOptions.pie.dataLabels.style
  43515. */
  43516. x: 0
  43517. },
  43518. /**
  43519. * If the total sum of the pie's values is 0, the series is represented
  43520. * as an empty circle . The `fillColor` option defines the color of that
  43521. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  43522. * the border thickness.
  43523. *
  43524. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43525. * Empty pie series
  43526. *
  43527. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43528. * @private
  43529. */
  43530. fillColor: void 0,
  43531. /**
  43532. * The end angle of the pie in degrees where 0 is top and 90 is right.
  43533. * Defaults to `startAngle` plus 360.
  43534. *
  43535. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  43536. * Semi-circle donut
  43537. *
  43538. * @type {number}
  43539. * @since 1.3.6
  43540. * @product highcharts highmaps
  43541. * @apioption plotOptions.pie.endAngle
  43542. */
  43543. /**
  43544. * Thickness describing the ring size for a donut type chart,
  43545. * overriding [innerSize](#plotOptions.pie.innerSize).
  43546. *
  43547. * @type {number}
  43548. * @default undefined
  43549. * @product highcharts
  43550. * @since 10.1.0
  43551. * @apioption plotOptions.pie.thickness
  43552. * @private
  43553. */
  43554. /**
  43555. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  43556. * this option tells whether the series shall be redrawn as if the
  43557. * hidden point were `null`.
  43558. *
  43559. * The default value changed from `false` to `true` with Highcharts
  43560. * 3.0.
  43561. *
  43562. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  43563. * True, the hiddden point is ignored
  43564. *
  43565. * @since 2.3.0
  43566. * @product highcharts highmaps
  43567. *
  43568. * @private
  43569. */
  43570. ignoreHiddenPoint: true,
  43571. /**
  43572. * @ignore-option
  43573. *
  43574. * @private
  43575. */
  43576. inactiveOtherPoints: true,
  43577. /**
  43578. * The size of the inner diameter for the pie. A size greater than 0
  43579. * renders a donut chart. Can be a percentage or pixel value.
  43580. * Percentages are relative to the pie size. Pixel values are given as
  43581. * integers. Setting overridden by thickness.
  43582. *
  43583. *
  43584. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  43585. * area, not the pie size.
  43586. *
  43587. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  43588. * 80px inner size
  43589. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  43590. * 50% of the plot area
  43591. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  43592. * 3D donut
  43593. *
  43594. * @type {number|string}
  43595. * @default 0
  43596. * @since 2.0
  43597. * @product highcharts highmaps
  43598. * @apioption plotOptions.pie.innerSize
  43599. */
  43600. /**
  43601. * @ignore-option
  43602. *
  43603. * @private
  43604. */
  43605. legendType: 'point',
  43606. /**
  43607. * @ignore-option
  43608. *
  43609. * @private
  43610. */
  43611. marker: null,
  43612. /**
  43613. * The minimum size for a pie in response to auto margins. The pie will
  43614. * try to shrink to make room for data labels in side the plot area,
  43615. * but only to this size.
  43616. *
  43617. * @type {number|string}
  43618. * @default 80
  43619. * @since 3.0
  43620. * @product highcharts highmaps
  43621. * @apioption plotOptions.pie.minSize
  43622. */
  43623. /**
  43624. * The diameter of the pie relative to the plot area. Can be a
  43625. * percentage or pixel value. Pixel values are given as integers. The
  43626. * default behaviour (as of 3.0) is to scale to the plot area and give
  43627. * room for data labels within the plot area.
  43628. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  43629. * default size calculation. As a consequence, the size of the pie may
  43630. * vary when points are updated and data labels more around. In that
  43631. * case it is best to set a fixed value, for example `"75%"`.
  43632. *
  43633. * @sample {highcharts} highcharts/plotoptions/pie-size/
  43634. * Smaller pie
  43635. *
  43636. * @type {number|string|null}
  43637. * @product highcharts highmaps
  43638. *
  43639. * @private
  43640. */
  43641. size: null,
  43642. /**
  43643. * Whether to display this particular series or series type in the
  43644. * legend. Since 2.1, pies are not shown in the legend by default.
  43645. *
  43646. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  43647. * One series in the legend, one hidden
  43648. *
  43649. * @product highcharts highmaps
  43650. *
  43651. * @private
  43652. */
  43653. showInLegend: false,
  43654. /**
  43655. * If a point is sliced, moved out from the center, how many pixels
  43656. * should it be moved?.
  43657. *
  43658. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  43659. * 20px offset
  43660. *
  43661. * @product highcharts highmaps
  43662. *
  43663. * @private
  43664. */
  43665. slicedOffset: 10,
  43666. /**
  43667. * The start angle of the pie slices in degrees where 0 is top and 90
  43668. * right.
  43669. *
  43670. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  43671. * Start from right
  43672. *
  43673. * @type {number}
  43674. * @default 0
  43675. * @since 2.3.4
  43676. * @product highcharts highmaps
  43677. * @apioption plotOptions.pie.startAngle
  43678. */
  43679. /**
  43680. * Sticky tracking of mouse events. When true, the `mouseOut` event
  43681. * on a series isn't triggered until the mouse moves over another
  43682. * series, or out of the plot area. When false, the `mouseOut` event on
  43683. * a series is triggered when the mouse leaves the area around the
  43684. * series' graph or markers. This also implies the tooltip. When
  43685. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  43686. * will be hidden when moving the mouse between series.
  43687. *
  43688. * @product highcharts highmaps
  43689. *
  43690. * @private
  43691. */
  43692. stickyTracking: false,
  43693. tooltip: {
  43694. followPointer: true
  43695. },
  43696. /**
  43697. * The color of the border surrounding each slice. When `null`, the
  43698. * border takes the same color as the slice fill. This can be used
  43699. * together with a `borderWidth` to fill drawing gaps created by
  43700. * antialiazing artefacts in borderless pies.
  43701. *
  43702. * In styled mode, the border stroke is given in the `.highcharts-point`
  43703. * class.
  43704. *
  43705. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  43706. * Black border
  43707. *
  43708. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43709. * @default #ffffff
  43710. * @product highcharts highmaps
  43711. *
  43712. * @private
  43713. */
  43714. borderColor: "#ffffff" /* Palette.backgroundColor */,
  43715. /**
  43716. * The width of the border surrounding each slice.
  43717. *
  43718. * When setting the border width to 0, there may be small gaps between
  43719. * the slices due to SVG antialiasing artefacts. To work around this,
  43720. * keep the border width at 0.5 or 1, but set the `borderColor` to
  43721. * `null` instead.
  43722. *
  43723. * In styled mode, the border stroke width is given in the
  43724. * `.highcharts-point` class.
  43725. *
  43726. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  43727. * 3px border
  43728. *
  43729. * @product highcharts highmaps
  43730. *
  43731. * @private
  43732. */
  43733. borderWidth: 1,
  43734. /**
  43735. * @ignore-option
  43736. * @private
  43737. */
  43738. lineWidth: void 0,
  43739. states: {
  43740. /**
  43741. * @extends plotOptions.series.states.hover
  43742. * @excluding marker, lineWidth, lineWidthPlus
  43743. * @product highcharts highmaps
  43744. */
  43745. hover: {
  43746. /**
  43747. * How much to brighten the point on interaction. Requires the
  43748. * main color to be defined in hex or rgb(a) format.
  43749. *
  43750. * In styled mode, the hover brightness is by default replaced
  43751. * by a fill-opacity given in the `.highcharts-point-hover`
  43752. * class.
  43753. *
  43754. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  43755. * Brightened by 0.5
  43756. *
  43757. * @product highcharts highmaps
  43758. */
  43759. brightness: 0.1
  43760. }
  43761. }
  43762. };
  43763. /**
  43764. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  43765. * it is inherited from [chart.type](#chart.type).
  43766. *
  43767. * @extends series,plotOptions.pie
  43768. * @excluding cropThreshold, dataParser, dataURL, linkedTo, stack, xAxis, yAxis,
  43769. * dataSorting, step, boostThreshold, boostBlending
  43770. * @product highcharts highmaps
  43771. * @apioption series.pie
  43772. */
  43773. /**
  43774. * An array of data points for the series. For the `pie` series type,
  43775. * points can be given in the following ways:
  43776. *
  43777. * 1. An array of numerical values. In this case, the numerical values will be
  43778. * interpreted as `y` options. Example:
  43779. * ```js
  43780. * data: [0, 5, 3, 5]
  43781. * ```
  43782. *
  43783. * 2. An array of objects with named values. The following snippet shows only a
  43784. * few settings, see the complete options set below. If the total number of
  43785. * data points exceeds the series'
  43786. * [turboThreshold](#series.pie.turboThreshold),
  43787. * this option is not available.
  43788. * ```js
  43789. * data: [{
  43790. * y: 1,
  43791. * name: "Point2",
  43792. * color: "#00FF00"
  43793. * }, {
  43794. * y: 7,
  43795. * name: "Point1",
  43796. * color: "#FF00FF"
  43797. * }]
  43798. * ```
  43799. *
  43800. * @sample {highcharts} highcharts/chart/reflow-true/
  43801. * Numerical values
  43802. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  43803. * Arrays of numeric x and y
  43804. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  43805. * Arrays of datetime x and y
  43806. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  43807. * Arrays of point.name and y
  43808. * @sample {highcharts} highcharts/series/data-array-of-objects/
  43809. * Config objects
  43810. *
  43811. * @type {Array<number|Array<string,(number|null)>|null|*>}
  43812. * @extends series.line.data
  43813. * @excluding marker, x
  43814. * @product highcharts highmaps
  43815. * @apioption series.pie.data
  43816. */
  43817. /**
  43818. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  43819. * @product highcharts highmaps
  43820. * @apioption series.pie.data.dataLabels
  43821. */
  43822. /**
  43823. * The sequential index of the data point in the legend.
  43824. *
  43825. * @type {number}
  43826. * @product highcharts highmaps
  43827. * @apioption series.pie.data.legendIndex
  43828. */
  43829. /**
  43830. * Whether to display a slice offset from the center.
  43831. *
  43832. * @sample {highcharts} highcharts/point/sliced/
  43833. * One sliced point
  43834. *
  43835. * @type {boolean}
  43836. * @product highcharts highmaps
  43837. * @apioption series.pie.data.sliced
  43838. */
  43839. /**
  43840. * @extends plotOptions.pie.dataLabels
  43841. * @excluding align, allowOverlap, inside, staggerLines, step
  43842. * @product highcharts highmaps
  43843. * @apioption series.pie.dataLabels
  43844. */
  43845. /**
  43846. * @excluding legendItemClick
  43847. * @product highcharts highmaps
  43848. * @apioption series.pie.events
  43849. */
  43850. ''; // placeholder for transpiled doclets above
  43851. /* *
  43852. *
  43853. * Default Export
  43854. *
  43855. * */
  43856. return PieSeriesDefaults;
  43857. });
  43858. _registerModule(_modules, 'Series/Pie/PieSeries.js', [_modules['Series/CenteredUtilities.js'], _modules['Series/Column/ColumnSeries.js'], _modules['Core/Globals.js'], _modules['Series/Pie/PiePoint.js'], _modules['Series/Pie/PieSeriesDefaults.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/Symbols.js'], _modules['Core/Utilities.js']], function (CU, ColumnSeries, H, PiePoint, PieSeriesDefaults, Series, SeriesRegistry, Symbols, U) {
  43859. /* *
  43860. *
  43861. * (c) 2010-2021 Torstein Honsi
  43862. *
  43863. * License: www.highcharts.com/license
  43864. *
  43865. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43866. *
  43867. * */
  43868. const { getStartAndEndRadians } = CU;
  43869. const { noop } = H;
  43870. const { clamp, extend, fireEvent, merge, pick, relativeLength } = U;
  43871. /* *
  43872. *
  43873. * Class
  43874. *
  43875. * */
  43876. /**
  43877. * Pie series type.
  43878. *
  43879. * @private
  43880. * @class
  43881. * @name Highcharts.seriesTypes.pie
  43882. *
  43883. * @augments Highcharts.Series
  43884. */
  43885. class PieSeries extends Series {
  43886. constructor() {
  43887. /* *
  43888. *
  43889. * Static Properties
  43890. *
  43891. * */
  43892. super(...arguments);
  43893. /* *
  43894. *
  43895. * Properties
  43896. *
  43897. * */
  43898. this.center = void 0;
  43899. this.data = void 0;
  43900. this.maxLabelDistance = void 0;
  43901. this.options = void 0;
  43902. this.points = void 0;
  43903. /* eslint-enable valid-jsdoc */
  43904. }
  43905. /* *
  43906. *
  43907. * Functions
  43908. *
  43909. * */
  43910. /* eslint-disable valid-jsdoc */
  43911. /**
  43912. * Animates the pies in.
  43913. * @private
  43914. */
  43915. animate(init) {
  43916. const series = this, points = series.points, startAngleRad = series.startAngleRad;
  43917. if (!init) {
  43918. points.forEach(function (point) {
  43919. const graphic = point.graphic, args = point.shapeArgs;
  43920. if (graphic && args) {
  43921. // start values
  43922. graphic.attr({
  43923. // animate from inner radius (#779)
  43924. r: pick(point.startR, (series.center && series.center[3] / 2)),
  43925. start: startAngleRad,
  43926. end: startAngleRad
  43927. });
  43928. // animate
  43929. graphic.animate({
  43930. r: args.r,
  43931. start: args.start,
  43932. end: args.end
  43933. }, series.options.animation);
  43934. }
  43935. });
  43936. }
  43937. }
  43938. /**
  43939. * Called internally to draw auxiliary graph in pie-like series in
  43940. * situtation when the default graph is not sufficient enough to present
  43941. * the data well. Auxiliary graph is saved in the same object as
  43942. * regular graph.
  43943. * @private
  43944. */
  43945. drawEmpty() {
  43946. const start = this.startAngleRad, end = this.endAngleRad, options = this.options;
  43947. let centerX, centerY;
  43948. // Draw auxiliary graph if there're no visible points.
  43949. if (this.total === 0 && this.center) {
  43950. centerX = this.center[0];
  43951. centerY = this.center[1];
  43952. if (!this.graph) {
  43953. this.graph = this.chart.renderer
  43954. .arc(centerX, centerY, this.center[1] / 2, 0, start, end)
  43955. .addClass('highcharts-empty-series')
  43956. .add(this.group);
  43957. }
  43958. this.graph.attr({
  43959. d: Symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
  43960. start,
  43961. end,
  43962. innerR: this.center[3] / 2
  43963. })
  43964. });
  43965. if (!this.chart.styledMode) {
  43966. this.graph.attr({
  43967. 'stroke-width': options.borderWidth,
  43968. fill: options.fillColor || 'none',
  43969. stroke: options.color || "#cccccc" /* Palette.neutralColor20 */
  43970. });
  43971. }
  43972. }
  43973. else if (this.graph) { // Destroy the graph object.
  43974. this.graph = this.graph.destroy();
  43975. }
  43976. }
  43977. /**
  43978. * Slices in pie chart are initialized in DOM, but it's shapes and
  43979. * animations are normally run in `drawPoints()`.
  43980. * @private
  43981. */
  43982. drawPoints() {
  43983. const renderer = this.chart.renderer;
  43984. this.points.forEach(function (point) {
  43985. // When updating a series between 2d and 3d or cartesian and
  43986. // polar, the shape type changes.
  43987. if (point.graphic && point.hasNewShapeType()) {
  43988. point.graphic = point.graphic.destroy();
  43989. }
  43990. if (!point.graphic) {
  43991. point.graphic = renderer[point.shapeType](point.shapeArgs)
  43992. .add(point.series.group);
  43993. point.delayedRendering = true;
  43994. }
  43995. });
  43996. }
  43997. /**
  43998. * Extend the generatePoints method by adding total and percentage
  43999. * properties to each point
  44000. * @private
  44001. */
  44002. generatePoints() {
  44003. super.generatePoints();
  44004. this.updateTotals();
  44005. }
  44006. /**
  44007. * Utility for getting the x value from a given y, used for
  44008. * anticollision logic in data labels. Added point for using specific
  44009. * points' label distance.
  44010. * @private
  44011. */
  44012. getX(y, left, point) {
  44013. const center = this.center,
  44014. // Variable pie has individual radius
  44015. radius = this.radii ?
  44016. this.radii[point.index] || 0 :
  44017. center[2] / 2;
  44018. const angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  44019. const x = center[0] +
  44020. (left ? -1 : 1) *
  44021. (Math.cos(angle) * (radius + point.labelDistance)) +
  44022. (point.labelDistance > 0 ?
  44023. (left ? -1 : 1) * this.options.dataLabels.padding :
  44024. 0);
  44025. return x;
  44026. }
  44027. /**
  44028. * Define hasData function for non-cartesian series. Returns true if the
  44029. * series has points at all.
  44030. * @private
  44031. */
  44032. hasData() {
  44033. return !!this.processedXData.length; // != 0
  44034. }
  44035. /**
  44036. * Draw the data points
  44037. * @private
  44038. */
  44039. redrawPoints() {
  44040. const series = this, chart = series.chart;
  44041. let groupTranslation, graphic, pointAttr, shapeArgs;
  44042. this.drawEmpty();
  44043. // Apply the drop-shadow to the group because otherwise each element
  44044. // would cast a shadow on others
  44045. if (series.group && !chart.styledMode) {
  44046. series.group.shadow(series.options.shadow);
  44047. }
  44048. // draw the slices
  44049. series.points.forEach(function (point) {
  44050. const animateTo = {};
  44051. graphic = point.graphic;
  44052. if (!point.isNull && graphic) {
  44053. shapeArgs = point.shapeArgs;
  44054. // If the point is sliced, use special translation, else use
  44055. // plot area translation
  44056. groupTranslation = point.getTranslate();
  44057. if (!chart.styledMode) {
  44058. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  44059. }
  44060. // Draw the slice
  44061. if (!point.delayedRendering) {
  44062. graphic
  44063. .setRadialReference(series.center);
  44064. if (!chart.styledMode) {
  44065. merge(true, animateTo, pointAttr);
  44066. }
  44067. merge(true, animateTo, shapeArgs, groupTranslation);
  44068. graphic.animate(animateTo);
  44069. }
  44070. else {
  44071. graphic
  44072. .setRadialReference(series.center)
  44073. .attr(shapeArgs)
  44074. .attr(groupTranslation);
  44075. if (!chart.styledMode) {
  44076. graphic
  44077. .attr(pointAttr)
  44078. .attr({ 'stroke-linejoin': 'round' });
  44079. }
  44080. point.delayedRendering = false;
  44081. }
  44082. graphic
  44083. .attr({
  44084. visibility: point.visible ? 'inherit' : 'hidden'
  44085. });
  44086. graphic.addClass(point.getClassName(), true);
  44087. }
  44088. else if (graphic) {
  44089. point.graphic = graphic.destroy();
  44090. }
  44091. });
  44092. }
  44093. /**
  44094. * Utility for sorting data labels.
  44095. * @private
  44096. */
  44097. sortByAngle(points, sign) {
  44098. points.sort(function (a, b) {
  44099. return ((typeof a.angle !== 'undefined') &&
  44100. (b.angle - a.angle) * sign);
  44101. });
  44102. }
  44103. /**
  44104. * Do translation for pie slices
  44105. * @private
  44106. */
  44107. translate(positions) {
  44108. fireEvent(this, 'translate');
  44109. this.generatePoints();
  44110. const series = this, precision = 1000, // issue #172
  44111. options = series.options, slicedOffset = options.slicedOffset, connectorOffset = slicedOffset + (options.borderWidth || 0), radians = getStartAndEndRadians(options.startAngle, options.endAngle), startAngleRad = series.startAngleRad = radians.start, endAngleRad = series.endAngleRad = radians.end, circ = endAngleRad - startAngleRad, // 2 * Math.PI,
  44112. points = series.points, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, len = points.length;
  44113. let finalConnectorOffset, start, end, angle,
  44114. // the x component of the radius vector for a given point
  44115. radiusX, radiusY, i, point, cumulative = 0;
  44116. // Get positions - either an integer or a percentage string must be
  44117. // given. If positions are passed as a parameter, we're in a
  44118. // recursive loop for adjusting space for data labels.
  44119. if (!positions) {
  44120. series.center = positions = series.getCenter();
  44121. }
  44122. // Calculate the geometry for each point
  44123. for (i = 0; i < len; i++) {
  44124. point = points[i];
  44125. // set start and end angle
  44126. start = startAngleRad + (cumulative * circ);
  44127. if (point.isValid() &&
  44128. (!ignoreHiddenPoint || point.visible)) {
  44129. cumulative += point.percentage / 100;
  44130. }
  44131. end = startAngleRad + (cumulative * circ);
  44132. // set the shape
  44133. const shapeArgs = {
  44134. x: positions[0],
  44135. y: positions[1],
  44136. r: positions[2] / 2,
  44137. innerR: positions[3] / 2,
  44138. start: Math.round(start * precision) / precision,
  44139. end: Math.round(end * precision) / precision
  44140. };
  44141. point.shapeType = 'arc';
  44142. point.shapeArgs = shapeArgs;
  44143. // Used for distance calculation for specific point.
  44144. point.labelDistance = pick((point.options.dataLabels &&
  44145. point.options.dataLabels.distance), labelDistance);
  44146. // Compute point.labelDistance if it's defined as percentage
  44147. // of slice radius (#8854)
  44148. point.labelDistance = relativeLength(point.labelDistance, shapeArgs.r);
  44149. // Saved for later dataLabels distance calculation.
  44150. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  44151. // The angle must stay within -90 and 270 (#2645)
  44152. angle = (end + start) / 2;
  44153. if (angle > 1.5 * Math.PI) {
  44154. angle -= 2 * Math.PI;
  44155. }
  44156. else if (angle < -Math.PI / 2) {
  44157. angle += 2 * Math.PI;
  44158. }
  44159. // Center for the sliced out slice
  44160. point.slicedTranslation = {
  44161. translateX: Math.round(Math.cos(angle) * slicedOffset),
  44162. translateY: Math.round(Math.sin(angle) * slicedOffset)
  44163. };
  44164. // set the anchor point for tooltips
  44165. radiusX = Math.cos(angle) * positions[2] / 2;
  44166. radiusY = Math.sin(angle) * positions[2] / 2;
  44167. point.tooltipPos = [
  44168. positions[0] + radiusX * 0.7,
  44169. positions[1] + radiusY * 0.7
  44170. ];
  44171. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  44172. 1 :
  44173. 0;
  44174. point.angle = angle;
  44175. // Set the anchor point for data labels. Use point.labelDistance
  44176. // instead of labelDistance // #1174
  44177. // finalConnectorOffset - not override connectorOffset value.
  44178. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  44179. point.labelPosition = {
  44180. natural: {
  44181. // initial position of the data label - it's utilized for
  44182. // finding the final position for the label
  44183. x: positions[0] + radiusX + Math.cos(angle) *
  44184. point.labelDistance,
  44185. y: positions[1] + radiusY + Math.sin(angle) *
  44186. point.labelDistance
  44187. },
  44188. computed: {
  44189. // used for generating connector path -
  44190. // initialized later in drawDataLabels function
  44191. // x: undefined,
  44192. // y: undefined
  44193. },
  44194. // left - pie on the left side of the data label
  44195. // right - pie on the right side of the data label
  44196. // center - data label overlaps the pie
  44197. alignment: point.labelDistance < 0 ?
  44198. 'center' : point.half ? 'right' : 'left',
  44199. connectorPosition: {
  44200. breakAt: {
  44201. x: positions[0] + radiusX + Math.cos(angle) *
  44202. finalConnectorOffset,
  44203. y: positions[1] + radiusY + Math.sin(angle) *
  44204. finalConnectorOffset
  44205. },
  44206. touchingSliceAt: {
  44207. x: positions[0] + radiusX,
  44208. y: positions[1] + radiusY
  44209. }
  44210. }
  44211. };
  44212. }
  44213. fireEvent(series, 'afterTranslate');
  44214. }
  44215. /**
  44216. * Recompute total chart sum and update percentages of points.
  44217. * @private
  44218. */
  44219. updateTotals() {
  44220. const points = this.points, len = points.length, ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  44221. let i, point, total = 0;
  44222. // Get the total sum
  44223. for (i = 0; i < len; i++) {
  44224. point = points[i];
  44225. if (point.isValid() &&
  44226. (!ignoreHiddenPoint || point.visible)) {
  44227. total += point.y;
  44228. }
  44229. }
  44230. this.total = total;
  44231. // Set each point's properties
  44232. for (i = 0; i < len; i++) {
  44233. point = points[i];
  44234. point.percentage =
  44235. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  44236. point.y / total * 100 :
  44237. 0;
  44238. point.total = total;
  44239. }
  44240. }
  44241. }
  44242. PieSeries.defaultOptions = merge(Series.defaultOptions, PieSeriesDefaults);
  44243. extend(PieSeries.prototype, {
  44244. axisTypes: [],
  44245. directTouch: true,
  44246. drawGraph: void 0,
  44247. drawTracker: ColumnSeries.prototype.drawTracker,
  44248. getCenter: CU.getCenter,
  44249. getSymbol: noop,
  44250. isCartesian: false,
  44251. noSharedTooltip: true,
  44252. pointAttribs: ColumnSeries.prototype.pointAttribs,
  44253. pointClass: PiePoint,
  44254. requireSorting: false,
  44255. searchPoint: noop,
  44256. trackerGroups: ['group', 'dataLabelsGroup']
  44257. });
  44258. SeriesRegistry.registerSeriesType('pie', PieSeries);
  44259. /* *
  44260. *
  44261. * Default Export
  44262. *
  44263. * */
  44264. return PieSeries;
  44265. });
  44266. _registerModule(_modules, 'Series/Pie/PieDataLabel.js', [_modules['Core/Series/DataLabel.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DataLabel, H, R, SeriesRegistry, U) {
  44267. /* *
  44268. *
  44269. * (c) 2010-2021 Torstein Honsi
  44270. *
  44271. * License: www.highcharts.com/license
  44272. *
  44273. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44274. *
  44275. * */
  44276. const { noop } = H;
  44277. const { distribute } = R;
  44278. const { series: Series } = SeriesRegistry;
  44279. const { arrayMax, clamp, defined, merge, pick, relativeLength } = U;
  44280. /* *
  44281. *
  44282. * Composition
  44283. *
  44284. * */
  44285. var ColumnDataLabel;
  44286. (function (ColumnDataLabel) {
  44287. /* *
  44288. *
  44289. * Constants
  44290. *
  44291. * */
  44292. const composedMembers = [];
  44293. const dataLabelPositioners = {
  44294. // Based on the value computed in Highcharts' distribute algorithm.
  44295. radialDistributionY: function (point) {
  44296. return point.top + point.distributeBox.pos;
  44297. },
  44298. // get the x - use the natural x position for labels near the
  44299. // top and bottom, to prevent the top and botton slice
  44300. // connectors from touching each other on either side
  44301. // Based on the value computed in Highcharts' distribute algorithm.
  44302. radialDistributionX: function (series, point, y, naturalY) {
  44303. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  44304. naturalY :
  44305. y, point.half, point);
  44306. },
  44307. // dataLabels.distance determines the x position of the label
  44308. justify: function (point, radius, seriesCenter) {
  44309. return seriesCenter[0] + (point.half ? -1 : 1) *
  44310. (radius + point.labelDistance);
  44311. },
  44312. // Left edges of the left-half labels touch the left edge of the plot
  44313. // area. Right edges of the right-half labels touch the right edge of
  44314. // the plot area.
  44315. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  44316. const dataLabelWidth = dataLabel.getBBox().width;
  44317. return half ? dataLabelWidth + plotLeft :
  44318. plotWidth - dataLabelWidth - plotLeft;
  44319. },
  44320. // Connectors of each side end in the same x position. Labels are
  44321. // aligned to them. Left edge of the widest left-half label touches the
  44322. // left edge of the plot area. Right edge of the widest right-half label
  44323. // touches the right edge of the plot area.
  44324. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  44325. let maxDataLabelWidth = 0, dataLabelWidth;
  44326. // find widest data label
  44327. points.forEach(function (point) {
  44328. dataLabelWidth = point.dataLabel.getBBox().width;
  44329. if (dataLabelWidth > maxDataLabelWidth) {
  44330. maxDataLabelWidth = dataLabelWidth;
  44331. }
  44332. });
  44333. return half ? maxDataLabelWidth + plotLeft :
  44334. plotWidth - maxDataLabelWidth - plotLeft;
  44335. }
  44336. };
  44337. /* *
  44338. *
  44339. * Functions
  44340. *
  44341. * */
  44342. /* eslint-disable valid-jsdoc */
  44343. /** @private */
  44344. function compose(PieSeriesClass) {
  44345. DataLabel.compose(Series);
  44346. if (U.pushUnique(composedMembers, PieSeriesClass)) {
  44347. const pieProto = PieSeriesClass.prototype;
  44348. pieProto.dataLabelPositioners = dataLabelPositioners;
  44349. pieProto.alignDataLabel = noop;
  44350. pieProto.drawDataLabels = drawDataLabels;
  44351. pieProto.placeDataLabels = placeDataLabels;
  44352. pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
  44353. }
  44354. }
  44355. ColumnDataLabel.compose = compose;
  44356. /**
  44357. * Override the base drawDataLabels method by pie specific functionality
  44358. * @private
  44359. */
  44360. function drawDataLabels() {
  44361. const series = this, data = series.data, chart = series.chart, options = series.options.dataLabels || {}, connectorPadding = options.connectorPadding, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], halves = [
  44362. [],
  44363. [] // left
  44364. ], overflow = [0, 0, 0, 0], // top, right, bottom, left
  44365. dataLabelPositioners = series.dataLabelPositioners;
  44366. let point, connectorWidth, connector, dataLabel, dataLabelWidth,
  44367. // labelPos,
  44368. labelPosition, labelHeight,
  44369. // divide the points into right and left halves for anti collision
  44370. x, y, visibility, j, pointDataLabelsOptions;
  44371. // get out if not enabled
  44372. if (!series.visible ||
  44373. (!options.enabled &&
  44374. !series._hasPointLabels)) {
  44375. return;
  44376. }
  44377. // Reset all labels that have been shortened
  44378. data.forEach(function (point) {
  44379. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  44380. point.dataLabel
  44381. .attr({
  44382. width: 'auto'
  44383. }).css({
  44384. width: 'auto',
  44385. textOverflow: 'clip'
  44386. });
  44387. point.dataLabel.shortened = false;
  44388. }
  44389. });
  44390. // run parent method
  44391. Series.prototype.drawDataLabels.apply(series);
  44392. data.forEach(function (point) {
  44393. if (point.dataLabel) {
  44394. if (point.visible) { // #407, #2510
  44395. // Arrange points for detection collision
  44396. halves[point.half].push(point);
  44397. // Reset positions (#4905)
  44398. point.dataLabel._pos = null;
  44399. // Avoid long labels squeezing the pie size too far down
  44400. if (!defined(options.style.width) &&
  44401. !defined(point.options.dataLabels &&
  44402. point.options.dataLabels.style &&
  44403. point.options.dataLabels.style.width)) {
  44404. if (point.dataLabel.getBBox().width > maxWidth) {
  44405. point.dataLabel.css({
  44406. // Use a fraction of the maxWidth to avoid
  44407. // wrapping close to the end of the string.
  44408. width: Math.round(maxWidth * 0.7) + 'px'
  44409. });
  44410. point.dataLabel.shortened = true;
  44411. }
  44412. }
  44413. }
  44414. else {
  44415. point.dataLabel = point.dataLabel.destroy();
  44416. // Workaround to make pies destroy multiple datalabels
  44417. // correctly. This logic needs rewriting to support multiple
  44418. // datalabels fully.
  44419. if (point.dataLabels && point.dataLabels.length === 1) {
  44420. delete point.dataLabels;
  44421. }
  44422. }
  44423. }
  44424. });
  44425. /* Loop over the points in each half, starting from the top and bottom
  44426. * of the pie to detect overlapping labels.
  44427. */
  44428. halves.forEach((points, i) => {
  44429. const length = points.length, positions = [];
  44430. let top, bottom, naturalY, sideOverflow, size = 0, distributionLength;
  44431. if (!length) {
  44432. return;
  44433. }
  44434. // Sort by angle
  44435. series.sortByAngle(points, i - 0.5);
  44436. // Only do anti-collision when we have dataLabels outside the pie
  44437. // and have connectors. (#856)
  44438. if (series.maxLabelDistance > 0) {
  44439. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  44440. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  44441. points.forEach(function (point) {
  44442. // check if specific points' label is outside the pie
  44443. if (point.labelDistance > 0 && point.dataLabel) {
  44444. // point.top depends on point.labelDistance value
  44445. // Used for calculation of y value in getX method
  44446. point.top = Math.max(0, centerY - radius - point.labelDistance);
  44447. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  44448. size = point.dataLabel.getBBox().height || 21;
  44449. // point.positionsIndex is needed for getting index of
  44450. // parameter related to specific point inside positions
  44451. // array - not every point is in positions array.
  44452. point.distributeBox = {
  44453. target: point.labelPosition.natural.y -
  44454. point.top + size / 2,
  44455. size,
  44456. rank: point.y
  44457. };
  44458. positions.push(point.distributeBox);
  44459. }
  44460. });
  44461. distributionLength = bottom + size - top;
  44462. distribute(positions, distributionLength, distributionLength / 5);
  44463. }
  44464. // Now the used slots are sorted, fill them up sequentially
  44465. for (j = 0; j < length; j++) {
  44466. point = points[j];
  44467. // labelPos = point.labelPos;
  44468. labelPosition = point.labelPosition;
  44469. dataLabel = point.dataLabel;
  44470. visibility = point.visible === false ? 'hidden' : 'inherit';
  44471. naturalY = labelPosition.natural.y;
  44472. y = naturalY;
  44473. if (positions && defined(point.distributeBox)) {
  44474. if (typeof point.distributeBox.pos === 'undefined') {
  44475. visibility = 'hidden';
  44476. }
  44477. else {
  44478. labelHeight = point.distributeBox.size;
  44479. // Find label's y position
  44480. y = dataLabelPositioners
  44481. .radialDistributionY(point);
  44482. }
  44483. }
  44484. // It is needed to delete point.positionIndex for
  44485. // dynamically added points etc.
  44486. delete point.positionIndex; // @todo unused
  44487. // Find label's x position
  44488. // justify is undocumented in the API - preserve support for it
  44489. if (options.justify) {
  44490. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  44491. }
  44492. else {
  44493. switch (options.alignTo) {
  44494. case 'connectors':
  44495. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  44496. break;
  44497. case 'plotEdges':
  44498. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  44499. break;
  44500. default:
  44501. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  44502. }
  44503. }
  44504. // Record the placement and visibility
  44505. dataLabel._attr = {
  44506. visibility: visibility,
  44507. align: labelPosition.alignment
  44508. };
  44509. pointDataLabelsOptions = point.options.dataLabels || {};
  44510. dataLabel._pos = {
  44511. x: (x +
  44512. pick(pointDataLabelsOptions.x, options.x) + // (#12985)
  44513. ({
  44514. left: connectorPadding,
  44515. right: -connectorPadding
  44516. }[labelPosition.alignment] || 0)),
  44517. y: (y +
  44518. pick(pointDataLabelsOptions.y, options.y) - // (#12985)
  44519. // Vertically center
  44520. dataLabel.getBBox().height / 2)
  44521. };
  44522. // labelPos.x = x;
  44523. // labelPos.y = y;
  44524. if (labelPosition) {
  44525. labelPosition.computed.x = x;
  44526. labelPosition.computed.y = y;
  44527. }
  44528. // Detect overflowing data labels
  44529. if (pick(options.crop, true)) {
  44530. dataLabelWidth = dataLabel.getBBox().width;
  44531. sideOverflow = null;
  44532. // Overflow left
  44533. if (x - dataLabelWidth < connectorPadding &&
  44534. i === 1 // left half
  44535. ) {
  44536. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  44537. overflow[3] = Math.max(sideOverflow, overflow[3]);
  44538. // Overflow right
  44539. }
  44540. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  44541. i === 0 // right half
  44542. ) {
  44543. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  44544. overflow[1] = Math.max(sideOverflow, overflow[1]);
  44545. }
  44546. // Overflow top
  44547. if (y - labelHeight / 2 < 0) {
  44548. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  44549. // Overflow left
  44550. }
  44551. else if (y + labelHeight / 2 > plotHeight) {
  44552. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  44553. }
  44554. dataLabel.sideOverflow = sideOverflow;
  44555. }
  44556. } // for each point
  44557. }); // for each half
  44558. // Do not apply the final placement and draw the connectors until we
  44559. // have verified that labels are not spilling over.
  44560. if (arrayMax(overflow) === 0 ||
  44561. this.verifyDataLabelOverflow(overflow)) {
  44562. // Place the labels in the final position
  44563. this.placeDataLabels();
  44564. this.points.forEach(function (point) {
  44565. // #8864: every connector can have individual options
  44566. pointDataLabelsOptions =
  44567. merge(options, point.options.dataLabels);
  44568. connectorWidth =
  44569. pick(pointDataLabelsOptions.connectorWidth, 1);
  44570. // Draw the connector
  44571. if (connectorWidth) {
  44572. let isNew;
  44573. connector = point.connector;
  44574. dataLabel = point.dataLabel;
  44575. if (dataLabel &&
  44576. dataLabel._pos &&
  44577. point.visible &&
  44578. point.labelDistance > 0) {
  44579. visibility = dataLabel._attr.visibility;
  44580. isNew = !connector;
  44581. if (isNew) {
  44582. point.connector = connector = chart.renderer
  44583. .path()
  44584. .addClass('highcharts-data-label-connector ' +
  44585. ' highcharts-color-' + point.colorIndex +
  44586. (point.className ?
  44587. ' ' + point.className :
  44588. ''))
  44589. .add(series.dataLabelsGroup);
  44590. if (!chart.styledMode) {
  44591. connector.attr({
  44592. 'stroke-width': connectorWidth,
  44593. 'stroke': (pointDataLabelsOptions.connectorColor ||
  44594. point.color ||
  44595. "#666666" /* Palette.neutralColor60 */)
  44596. });
  44597. }
  44598. }
  44599. connector[isNew ? 'attr' : 'animate']({
  44600. d: point.getConnectorPath()
  44601. });
  44602. connector.attr('visibility', visibility);
  44603. }
  44604. else if (connector) {
  44605. point.connector = connector.destroy();
  44606. }
  44607. }
  44608. });
  44609. }
  44610. }
  44611. /**
  44612. * Perform the final placement of the data labels after we have verified
  44613. * that they fall within the plot area.
  44614. * @private
  44615. */
  44616. function placeDataLabels() {
  44617. this.points.forEach(function (point) {
  44618. let dataLabel = point.dataLabel, _pos;
  44619. if (dataLabel && point.visible) {
  44620. _pos = dataLabel._pos;
  44621. if (_pos) {
  44622. // Shorten data labels with ellipsis if they still overflow
  44623. // after the pie has reached minSize (#223).
  44624. if (dataLabel.sideOverflow) {
  44625. dataLabel._attr.width =
  44626. Math.max(dataLabel.getBBox().width -
  44627. dataLabel.sideOverflow, 0);
  44628. dataLabel.css({
  44629. width: dataLabel._attr.width + 'px',
  44630. textOverflow: ((this.options.dataLabels.style || {})
  44631. .textOverflow ||
  44632. 'ellipsis')
  44633. });
  44634. dataLabel.shortened = true;
  44635. }
  44636. dataLabel.attr(dataLabel._attr);
  44637. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  44638. dataLabel.moved = true;
  44639. }
  44640. else if (dataLabel) {
  44641. dataLabel.attr({ y: -9999 });
  44642. }
  44643. }
  44644. // Clear for update
  44645. delete point.distributeBox;
  44646. }, this);
  44647. }
  44648. /**
  44649. * Verify whether the data labels are allowed to draw, or we should run more
  44650. * translation and data label positioning to keep them inside the plot area.
  44651. * Returns true when data labels are ready to draw.
  44652. * @private
  44653. */
  44654. function verifyDataLabelOverflow(overflow) {
  44655. let center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
  44656. // If a size is set, return true and don't try to shrink the pie
  44657. // to fit the labels.
  44658. ret = options.size !== null;
  44659. if (!ret) {
  44660. // Handle horizontal size and center
  44661. if (centerOption[0] !== null) { // Fixed center
  44662. newSize = Math.max(center[2] -
  44663. Math.max(overflow[1], overflow[3]), minSize);
  44664. }
  44665. else { // Auto center
  44666. newSize = Math.max(
  44667. // horizontal overflow
  44668. center[2] - overflow[1] - overflow[3], minSize);
  44669. // horizontal center
  44670. center[0] += (overflow[3] - overflow[1]) / 2;
  44671. }
  44672. // Handle vertical size and center
  44673. if (centerOption[1] !== null) { // Fixed center
  44674. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  44675. }
  44676. else { // Auto center
  44677. newSize = clamp(newSize, minSize,
  44678. // vertical overflow
  44679. center[2] - overflow[0] - overflow[2]);
  44680. // vertical center
  44681. center[1] += (overflow[0] - overflow[2]) / 2;
  44682. }
  44683. // If the size must be decreased, we need to run translate and
  44684. // drawDataLabels again
  44685. if (newSize < center[2]) {
  44686. center[2] = newSize;
  44687. center[3] = Math.min(// #3632
  44688. options.thickness ?
  44689. Math.max(0, newSize - options.thickness * 2) :
  44690. Math.max(0, relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
  44691. this.translate(center);
  44692. if (this.drawDataLabels) {
  44693. this.drawDataLabels();
  44694. }
  44695. // Else, return true to indicate that the pie and its labels is
  44696. // within the plot area
  44697. }
  44698. else {
  44699. ret = true;
  44700. }
  44701. }
  44702. return ret;
  44703. }
  44704. })(ColumnDataLabel || (ColumnDataLabel = {}));
  44705. /* *
  44706. *
  44707. * Default Export
  44708. *
  44709. * */
  44710. return ColumnDataLabel;
  44711. });
  44712. _registerModule(_modules, 'Extensions/OverlappingDataLabels.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Chart, U) {
  44713. /* *
  44714. *
  44715. * Highcharts module to hide overlapping data labels.
  44716. * This module is included in Highcharts.
  44717. *
  44718. * (c) 2009-2021 Torstein Honsi
  44719. *
  44720. * License: www.highcharts.com/license
  44721. *
  44722. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44723. *
  44724. * */
  44725. const { addEvent, fireEvent, isArray, isNumber, objectEach, pick } = U;
  44726. /**
  44727. * Internal type
  44728. * @private
  44729. */
  44730. /* eslint-disable no-invalid-this */
  44731. // Collect potensial overlapping data labels. Stack labels probably don't need
  44732. // to be considered because they are usually accompanied by data labels that lie
  44733. // inside the columns.
  44734. addEvent(Chart, 'render', function collectAndHide() {
  44735. let chart = this, labels = [];
  44736. // Consider external label collectors
  44737. (this.labelCollectors || []).forEach(function (collector) {
  44738. labels = labels.concat(collector());
  44739. });
  44740. (this.yAxis || []).forEach(function (yAxis) {
  44741. if (yAxis.stacking &&
  44742. yAxis.options.stackLabels &&
  44743. !yAxis.options.stackLabels.allowOverlap) {
  44744. objectEach(yAxis.stacking.stacks, function (stack) {
  44745. objectEach(stack, function (stackItem) {
  44746. if (stackItem.label) {
  44747. labels.push(stackItem.label);
  44748. }
  44749. });
  44750. });
  44751. }
  44752. });
  44753. (this.series || []).forEach(function (series) {
  44754. const dlOptions = series.options.dataLabels;
  44755. if (series.visible &&
  44756. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  44757. const push = (points) => points.forEach((point) => {
  44758. if (point.visible) {
  44759. const dataLabels = (isArray(point.dataLabels) ?
  44760. point.dataLabels :
  44761. (point.dataLabel ? [point.dataLabel] : []));
  44762. dataLabels.forEach(function (label) {
  44763. const options = label.options;
  44764. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  44765. if (!options.allowOverlap) {
  44766. labels.push(label);
  44767. }
  44768. else { // #13449
  44769. label.oldOpacity = label.opacity;
  44770. label.newOpacity = 1;
  44771. hideOrShow(label, chart);
  44772. }
  44773. });
  44774. }
  44775. });
  44776. push(series.nodes || []);
  44777. push(series.points);
  44778. }
  44779. });
  44780. this.hideOverlappingLabels(labels);
  44781. });
  44782. /**
  44783. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  44784. * provide a smooth visual imression.
  44785. *
  44786. * @private
  44787. * @function Highcharts.Chart#hideOverlappingLabels
  44788. * @param {Array<Highcharts.SVGElement>} labels
  44789. * Rendered data labels
  44790. * @requires modules/overlapping-datalabels
  44791. */
  44792. Chart.prototype.hideOverlappingLabels = function (labels) {
  44793. let chart = this, len = labels.length, ren = chart.renderer, label, i, j, label1, label2, box1, box2, isLabelAffected = false, isIntersectRect = function (box1, box2) {
  44794. return !(box2.x >= box1.x + box1.width ||
  44795. box2.x + box2.width <= box1.x ||
  44796. box2.y >= box1.y + box1.height ||
  44797. box2.y + box2.height <= box1.y);
  44798. },
  44799. // Get the box with its position inside the chart, as opposed to getBBox
  44800. // that only reports the position relative to the parent.
  44801. getAbsoluteBox = function (label) {
  44802. let pos, parent, bBox,
  44803. // Substract the padding if no background or border (#4333)
  44804. padding = label.box ? 0 : (label.padding || 0), lineHeightCorrection = 0, xOffset = 0, boxWidth, alignValue;
  44805. if (label &&
  44806. (!label.alignAttr || label.placed)) {
  44807. pos = label.alignAttr || {
  44808. x: label.attr('x'),
  44809. y: label.attr('y')
  44810. };
  44811. parent = label.parentGroup;
  44812. // Get width and height if pure text nodes (stack labels)
  44813. if (!label.width) {
  44814. bBox = label.getBBox();
  44815. label.width = bBox.width;
  44816. label.height = bBox.height;
  44817. // Labels positions are computed from top left corner, so we
  44818. // need to substract the text height from text nodes too.
  44819. lineHeightCorrection = ren.fontMetrics(label.element).h;
  44820. }
  44821. boxWidth = label.width - 2 * padding;
  44822. alignValue = {
  44823. left: '0',
  44824. center: '0.5',
  44825. right: '1'
  44826. }[label.alignValue];
  44827. if (alignValue) {
  44828. xOffset = +alignValue * boxWidth;
  44829. }
  44830. else if (isNumber(label.x) &&
  44831. Math.round(label.x) !== label.translateX) {
  44832. xOffset = label.x - label.translateX;
  44833. }
  44834. return {
  44835. x: pos.x + (parent.translateX || 0) + padding -
  44836. (xOffset || 0),
  44837. y: pos.y + (parent.translateY || 0) + padding -
  44838. lineHeightCorrection,
  44839. width: label.width - 2 * padding,
  44840. height: label.height - 2 * padding
  44841. };
  44842. }
  44843. };
  44844. for (i = 0; i < len; i++) {
  44845. label = labels[i];
  44846. if (label) {
  44847. // Mark with initial opacity
  44848. label.oldOpacity = label.opacity;
  44849. label.newOpacity = 1;
  44850. label.absoluteBox = getAbsoluteBox(label);
  44851. }
  44852. }
  44853. // Prevent a situation in a gradually rising slope, that each label will
  44854. // hide the previous one because the previous one always has lower rank.
  44855. labels.sort(function (a, b) {
  44856. return (b.labelrank || 0) - (a.labelrank || 0);
  44857. });
  44858. // Detect overlapping labels
  44859. for (i = 0; i < len; i++) {
  44860. label1 = labels[i];
  44861. box1 = label1 && label1.absoluteBox;
  44862. for (j = i + 1; j < len; ++j) {
  44863. label2 = labels[j];
  44864. box2 = label2 && label2.absoluteBox;
  44865. if (box1 &&
  44866. box2 &&
  44867. label1 !== label2 && // #6465, polar chart with connectEnds
  44868. label1.newOpacity !== 0 &&
  44869. label2.newOpacity !== 0 &&
  44870. // #15863 dataLabels are no longer hidden by translation
  44871. label1.visibility !== 'hidden' &&
  44872. label2.visibility !== 'hidden') {
  44873. if (isIntersectRect(box1, box2)) {
  44874. (label1.labelrank < label2.labelrank ? label1 : label2)
  44875. .newOpacity = 0;
  44876. }
  44877. }
  44878. }
  44879. }
  44880. // Hide or show
  44881. labels.forEach(function (label) {
  44882. if (hideOrShow(label, chart)) {
  44883. isLabelAffected = true;
  44884. }
  44885. });
  44886. if (isLabelAffected) {
  44887. fireEvent(chart, 'afterHideAllOverlappingLabels');
  44888. }
  44889. };
  44890. /**
  44891. * Hide or show labels based on opacity.
  44892. *
  44893. * @private
  44894. * @function hideOrShow
  44895. * @param {Highcharts.SVGElement} label
  44896. * The label.
  44897. * @param {Highcharts.Chart} chart
  44898. * The chart that contains the label.
  44899. * @return {boolean}
  44900. * Whether label is affected
  44901. */
  44902. function hideOrShow(label, chart) {
  44903. let complete, newOpacity, isLabelAffected = false;
  44904. if (label) {
  44905. newOpacity = label.newOpacity;
  44906. if (label.oldOpacity !== newOpacity) {
  44907. // Make sure the label is completely hidden to avoid catching clicks
  44908. // (#4362)
  44909. if (label.alignAttr && label.placed) { // data labels
  44910. label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
  44911. complete = function () {
  44912. if (!chart.styledMode) {
  44913. label.css({
  44914. pointerEvents: newOpacity ? 'auto' : 'none'
  44915. });
  44916. }
  44917. };
  44918. isLabelAffected = true;
  44919. // Animate or set the opacity
  44920. label.alignAttr.opacity = newOpacity;
  44921. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  44922. fireEvent(chart, 'afterHideOverlappingLabel');
  44923. }
  44924. else { // other labels, tick labels
  44925. label.attr({
  44926. opacity: newOpacity
  44927. });
  44928. }
  44929. }
  44930. label.isOld = true;
  44931. }
  44932. return isLabelAffected;
  44933. }
  44934. });
  44935. _registerModule(_modules, 'Extensions/BorderRadius.js', [_modules['Core/Defaults.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (D, Series, SeriesRegistry, SVGElement, SVGRenderer, U) {
  44936. /* *
  44937. *
  44938. * Highcharts Border Radius module
  44939. *
  44940. * Author: Torstein Honsi
  44941. *
  44942. * License: www.highcharts.com/license
  44943. *
  44944. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44945. *
  44946. * */
  44947. const { defaultOptions } = D;
  44948. const { seriesTypes } = SeriesRegistry;
  44949. const { addEvent, extend, isObject, merge, relativeLength } = U;
  44950. /* *
  44951. *
  44952. * Constants
  44953. *
  44954. * */
  44955. const defaultBorderRadiusOptions = {
  44956. radius: 0,
  44957. scope: 'stack',
  44958. where: void 0
  44959. };
  44960. const optionsToObject = (options, seriesBROptions) => {
  44961. if (!isObject(options)) {
  44962. options = { radius: options || 0 };
  44963. }
  44964. return merge(defaultBorderRadiusOptions, seriesBROptions, options);
  44965. };
  44966. const applyBorderRadius = (path, i, r) => {
  44967. const a = path[i];
  44968. let b = path[i + 1];
  44969. if (b[0] === 'Z') {
  44970. b = path[0];
  44971. }
  44972. let line, arc, fromLineToArc;
  44973. // From straight line to arc
  44974. if ((a[0] === 'M' || a[0] === 'L') && b[0] === 'A') {
  44975. line = a;
  44976. arc = b;
  44977. fromLineToArc = true;
  44978. // From arc to straight line
  44979. }
  44980. else if (a[0] === 'A' && (b[0] === 'M' || b[0] === 'L')) {
  44981. line = b;
  44982. arc = a;
  44983. }
  44984. if (line && arc && arc.params) {
  44985. const bigR = arc[1],
  44986. // In our use cases, outer pie slice arcs are clockwise and inner
  44987. // arcs (donut/sunburst etc) are anti-clockwise
  44988. clockwise = arc[5], params = arc.params, { start, end, cx, cy } = params;
  44989. // Some geometric constants
  44990. const relativeR = clockwise ? (bigR - r) : (bigR + r),
  44991. // The angle, on the big arc, that the border radius arc takes up
  44992. angleOfBorderRadius = relativeR ? Math.asin(r / relativeR) : 0, angleOffset = clockwise ?
  44993. angleOfBorderRadius :
  44994. -angleOfBorderRadius,
  44995. // The distance along the radius of the big arc to the starting
  44996. // point of the small border radius arc
  44997. distanceBigCenterToStartArc = (Math.cos(angleOfBorderRadius) *
  44998. relativeR);
  44999. // From line to arc
  45000. if (fromLineToArc) {
  45001. // Update the cache
  45002. params.start = start + angleOffset;
  45003. // First move to the start position at the radial line. We want to
  45004. // start one borderRadius closer to the center.
  45005. line[1] = cx + distanceBigCenterToStartArc * Math.cos(start);
  45006. line[2] = cy + distanceBigCenterToStartArc * Math.sin(start);
  45007. // Now draw an arc towards the point where the small circle touches
  45008. // the great circle.
  45009. path.splice(i + 1, 0, [
  45010. 'A',
  45011. r,
  45012. r,
  45013. 0,
  45014. 0,
  45015. 1,
  45016. cx + bigR * Math.cos(params.start),
  45017. cy + bigR * Math.sin(params.start)
  45018. ]);
  45019. // From arc to line
  45020. }
  45021. else {
  45022. // Update the cache
  45023. params.end = end - angleOffset;
  45024. // End the big arc a bit earlier
  45025. arc[6] = cx + bigR * Math.cos(params.end);
  45026. arc[7] = cy + bigR * Math.sin(params.end);
  45027. // Draw a small arc towards a point on the end angle, but one
  45028. // borderRadius closer to the center relative to the perimeter.
  45029. path.splice(i + 1, 0, [
  45030. 'A',
  45031. r,
  45032. r,
  45033. 0,
  45034. 0,
  45035. 1,
  45036. cx + distanceBigCenterToStartArc * Math.cos(end),
  45037. cy + distanceBigCenterToStartArc * Math.sin(end)
  45038. ]);
  45039. }
  45040. // Long or short arc must be reconsidered because we have modified the
  45041. // start and end points
  45042. arc[4] = Math.abs(params.end - params.start) < Math.PI ? 0 : 1;
  45043. }
  45044. };
  45045. /* *
  45046. *
  45047. * Modifications
  45048. *
  45049. * */
  45050. // Check if the module has already been imported
  45051. // @todo implement as composition
  45052. if (SVGElement.symbolCustomAttribs.indexOf('borderRadius') === -1) {
  45053. SVGElement.symbolCustomAttribs.push('borderRadius', 'brBoxHeight', 'brBoxY');
  45054. // Extend arc with borderRadius
  45055. const arc = SVGRenderer.prototype.symbols.arc;
  45056. SVGRenderer.prototype.symbols.arc = function (x, y, w, h, options = {}) {
  45057. const path = arc(x, y, w, h, options), { innerR = 0, r = w, start = 0, end = 0 } = options;
  45058. if (options.open || !options.borderRadius) {
  45059. return path;
  45060. }
  45061. const alpha = end - start, sinHalfAlpha = Math.sin(alpha / 2), borderRadius = Math.max(Math.min(relativeLength(options.borderRadius || 0, r - innerR),
  45062. // Cap to half the sector radius
  45063. (r - innerR) / 2,
  45064. // For smaller pie slices, cap to the largest small circle that
  45065. // can be fitted within the sector
  45066. (r * sinHalfAlpha) / (1 + sinHalfAlpha)), 0),
  45067. // For the inner radius, we need an extra cap because the inner arc
  45068. // is shorter than the outer arc
  45069. innerBorderRadius = Math.min(borderRadius, 2 * (alpha / Math.PI) * innerR);
  45070. // Apply turn-by-turn border radius. Start at the end since we're
  45071. // splicing in arc segments.
  45072. let i = path.length - 1;
  45073. while (i--) {
  45074. applyBorderRadius(path, i, i > 1 ? innerBorderRadius : borderRadius);
  45075. }
  45076. return path;
  45077. };
  45078. // Extend roundedRect with individual cutting through rOffset
  45079. const roundedRect = SVGRenderer.prototype.symbols.roundedRect;
  45080. SVGRenderer.prototype.symbols.roundedRect = function (x, y, width, height, options = {}) {
  45081. const path = roundedRect(x, y, width, height, options), { r = 0, brBoxHeight = height, brBoxY = y } = options, brOffsetTop = y - brBoxY, brOffsetBtm = (brBoxY + brBoxHeight) - (y + height),
  45082. // When the distance to the border-radius box is greater than the r
  45083. // itself, it means no border radius. The -0.1 accounts for float
  45084. // rounding errors.
  45085. rTop = (brOffsetTop - r) > -0.1 ? 0 : r, rBtm = (brOffsetBtm - r) > -0.1 ? 0 : r, cutTop = Math.max(rTop && brOffsetTop, 0), cutBtm = Math.max(rBtm && brOffsetBtm, 0);
  45086. /*
  45087. The naming of control points:
  45088. / a -------- b \
  45089. / \
  45090. h c
  45091. | |
  45092. | |
  45093. | |
  45094. g d
  45095. \ /
  45096. \ f -------- e /
  45097. */
  45098. const a = [x + rTop, y], b = [x + width - rTop, y], c = [x + width, y + rTop], d = [
  45099. x + width, y + height - rBtm
  45100. ], e = [
  45101. x + width - rBtm,
  45102. y + height
  45103. ], f = [x + rBtm, y + height], g = [x, y + height - rBtm], h = [x, y + rTop];
  45104. const applyPythagoras = (r, altitude) => Math.sqrt(Math.pow(r, 2) - Math.pow(altitude, 2));
  45105. // Inside stacks, cut off part of the top
  45106. if (cutTop) {
  45107. const base = applyPythagoras(rTop, rTop - cutTop);
  45108. a[0] -= base;
  45109. b[0] += base;
  45110. c[1] = h[1] = y + rTop - cutTop;
  45111. }
  45112. // Column is lower than the radius. Cut off bottom inside the top
  45113. // radius.
  45114. if (height < rTop - cutTop) {
  45115. const base = applyPythagoras(rTop, rTop - cutTop - height);
  45116. c[0] = d[0] = x + width - rTop + base;
  45117. e[0] = Math.min(c[0], e[0]);
  45118. f[0] = Math.max(d[0], f[0]);
  45119. g[0] = h[0] = x + rTop - base;
  45120. c[1] = h[1] = y + height;
  45121. }
  45122. // Inside stacks, cut off part of the bottom
  45123. if (cutBtm) {
  45124. const base = applyPythagoras(rBtm, rBtm - cutBtm);
  45125. e[0] += base;
  45126. f[0] -= base;
  45127. d[1] = g[1] = y + height - rBtm + cutBtm;
  45128. }
  45129. // Cut off top inside the bottom radius
  45130. if (height < rBtm - cutBtm) {
  45131. const base = applyPythagoras(rBtm, rBtm - cutBtm - height);
  45132. c[0] = d[0] = x + width - rBtm + base;
  45133. b[0] = Math.min(c[0], b[0]);
  45134. a[0] = Math.max(d[0], a[0]);
  45135. g[0] = h[0] = x + rBtm - base;
  45136. d[1] = g[1] = y;
  45137. }
  45138. // Preserve the box for data labels
  45139. path.length = 0;
  45140. path.push(['M', ...a],
  45141. // top side
  45142. ['L', ...b],
  45143. // top right corner
  45144. ['A', rTop, rTop, 0, 0, 1, ...c],
  45145. // right side
  45146. ['L', ...d],
  45147. // bottom right corner
  45148. ['A', rBtm, rBtm, 0, 0, 1, ...e],
  45149. // bottom side
  45150. ['L', ...f],
  45151. // bottom left corner
  45152. ['A', rBtm, rBtm, 0, 0, 1, ...g],
  45153. // left side
  45154. ['L', ...h],
  45155. // top left corner
  45156. ['A', rTop, rTop, 0, 0, 1, ...a], ['Z']);
  45157. return path;
  45158. };
  45159. addEvent(seriesTypes.pie, 'afterTranslate', function () {
  45160. const borderRadius = optionsToObject(this.options.borderRadius);
  45161. for (const point of this.points) {
  45162. const shapeArgs = point.shapeArgs;
  45163. if (shapeArgs) {
  45164. shapeArgs.borderRadius = relativeLength(borderRadius.radius, (shapeArgs.r || 0) - ((shapeArgs.innerR) || 0));
  45165. }
  45166. }
  45167. });
  45168. addEvent(Series, 'afterColumnTranslate', function () {
  45169. var _a,
  45170. _b;
  45171. if (this.options.borderRadius &&
  45172. !(this.chart.is3d && this.chart.is3d())) {
  45173. const { options, yAxis } = this, percent = options.stacking === 'percent', seriesDefault = (_b = (_a = defaultOptions.plotOptions) === null || _a === void 0 ? void 0 : _a[this.type]) === null || _b === void 0 ? void 0 : _b.borderRadius, borderRadius = optionsToObject(options.borderRadius, isObject(seriesDefault) ? seriesDefault : {}), reversed = yAxis.options.reversed;
  45174. for (const point of this.points) {
  45175. const { shapeArgs } = point;
  45176. if (point.shapeType === 'roundedRect' && shapeArgs) {
  45177. const { width = 0, height = 0, y = 0 } = shapeArgs;
  45178. let brBoxY = y, brBoxHeight = height;
  45179. // It would be nice to refactor StackItem.getStackBox/
  45180. // setOffset so that we could get a reliable box out of
  45181. // it. Currently it is close if we remove the label
  45182. // offset, but we still need to run crispCol and also
  45183. // flip it if inverted, so atm it is simpler to do it
  45184. // like the below.
  45185. if (borderRadius.scope === 'stack' &&
  45186. point.stackTotal) {
  45187. const stackEnd = yAxis.translate(percent ? 100 : point.stackTotal, false, true, false, true), stackThreshold = yAxis.translate(options.threshold || 0, false, true, false, true), box = this.crispCol(0, Math.min(stackEnd, stackThreshold), 0, Math.abs(stackEnd - stackThreshold));
  45188. brBoxY = box.y;
  45189. brBoxHeight = box.height;
  45190. }
  45191. const flip = (point.negative ? -1 : 1) *
  45192. (reversed ? -1 : 1) === -1;
  45193. // Handle the where option
  45194. let where = borderRadius.where;
  45195. // Waterfall, hanging columns should have rounding on
  45196. // all sides
  45197. if (!where &&
  45198. this.is('waterfall') &&
  45199. Math.abs((point.yBottom || 0) -
  45200. (this.translatedThreshold || 0)) > this.borderWidth) {
  45201. where = 'all';
  45202. }
  45203. if (!where) {
  45204. where = 'end';
  45205. }
  45206. // Get the radius
  45207. const r = Math.min(relativeLength(borderRadius.radius, width), width / 2,
  45208. // Cap to the height, but not if where is `end`
  45209. where === 'all' ? height / 2 : Infinity) || 0;
  45210. // If the `where` option is 'end', cut off the
  45211. // rectangles by making the border-radius box one r
  45212. // greater, so that the imaginary radius falls outside
  45213. // the rectangle.
  45214. if (where === 'end') {
  45215. if (flip) {
  45216. brBoxY -= r;
  45217. brBoxHeight += r;
  45218. }
  45219. else {
  45220. brBoxHeight += r;
  45221. }
  45222. }
  45223. extend(shapeArgs, { brBoxHeight, brBoxY, r });
  45224. }
  45225. }
  45226. }
  45227. }, {
  45228. // After columnrange and polar column modifications
  45229. order: 9
  45230. });
  45231. }
  45232. /* *
  45233. *
  45234. * Default Export
  45235. *
  45236. * */
  45237. const BorderRadius = {
  45238. optionsToObject
  45239. };
  45240. /* *
  45241. *
  45242. * API Declarations
  45243. *
  45244. * */
  45245. /**
  45246. * Detailed options for border radius.
  45247. *
  45248. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  45249. * Rounded columns
  45250. * @sample highcharts/plotoptions/series-border-radius
  45251. * Column and pie with rounded border
  45252. *
  45253. * @interface Highcharts.BorderRadiusOptionsObject
  45254. */ /**
  45255. * The border radius. A number signifies pixels. A percentage string, like for
  45256. * example `50%`, signifies a relative size. For columns this is relative to the
  45257. * column width, for pies it is relative to the radius and the inner radius.
  45258. *
  45259. * @name Highcharts.BorderRadiusOptionsObject#radius
  45260. * @type {string|number}
  45261. */ /**
  45262. * The scope of the rounding for column charts. In a stacked column chart, the
  45263. * value `point` means each single point will get rounded corners. The value
  45264. * `stack` means the rounding will apply to the full stack, so that only points
  45265. * close to the top or bottom will receive rounding.
  45266. *
  45267. * @name Highcharts.BorderRadiusOptionsObject#scope
  45268. * @validvalue ["point", "stack"]
  45269. * @type {string}
  45270. */ /**
  45271. * For column charts, where in the point or stack to apply rounding. The `end`
  45272. * value means only those corners at the point value will be rounded, leaving
  45273. * the corners at the base or threshold unrounded. This is the most intuitive
  45274. * behaviour. The `all` value means also the base will be rounded.
  45275. *
  45276. * @name Highcharts.BorderRadiusOptionsObject#where
  45277. * @validvalue ["all", "end"]
  45278. * @type {string}
  45279. * @default end
  45280. */
  45281. (''); // keeps doclets above in JS file
  45282. return BorderRadius;
  45283. });
  45284. _registerModule(_modules, 'Core/Responsive.js', [_modules['Core/Utilities.js']], function (U) {
  45285. /* *
  45286. *
  45287. * (c) 2010-2021 Torstein Honsi
  45288. *
  45289. * License: www.highcharts.com/license
  45290. *
  45291. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45292. *
  45293. * */
  45294. const { diffObjects, extend, find, isArray, isObject, merge, objectEach, pick, splat, uniqueKey } = U;
  45295. /* *
  45296. *
  45297. * Composition
  45298. *
  45299. * */
  45300. var Responsive;
  45301. (function (Responsive) {
  45302. /* *
  45303. *
  45304. * Declarations
  45305. *
  45306. * */
  45307. /* *
  45308. *
  45309. * Constants
  45310. *
  45311. * */
  45312. const composedMembers = [];
  45313. /* *
  45314. *
  45315. * Functions
  45316. *
  45317. * */
  45318. /**
  45319. * @private
  45320. */
  45321. function compose(ChartClass) {
  45322. if (U.pushUnique(composedMembers, ChartClass)) {
  45323. extend(ChartClass.prototype, {
  45324. matchResponsiveRule,
  45325. setResponsive
  45326. });
  45327. }
  45328. return ChartClass;
  45329. }
  45330. Responsive.compose = compose;
  45331. /**
  45332. * Handle a single responsiveness rule.
  45333. *
  45334. * @private
  45335. * @function Highcharts.Chart#matchResponsiveRule
  45336. * @param {Highcharts.ResponsiveRulesOptions} rule
  45337. * @param {Array<string>} matches
  45338. */
  45339. function matchResponsiveRule(rule, matches) {
  45340. const condition = rule.condition, fn = condition.callback || function () {
  45341. return (this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  45342. this.chartHeight <= pick(condition.maxHeight, Number.MAX_VALUE) &&
  45343. this.chartWidth >= pick(condition.minWidth, 0) &&
  45344. this.chartHeight >= pick(condition.minHeight, 0));
  45345. };
  45346. if (fn.call(this)) {
  45347. matches.push(rule._id);
  45348. }
  45349. }
  45350. /**
  45351. * Update the chart based on the current chart/document size and options
  45352. * for responsiveness.
  45353. *
  45354. * @private
  45355. * @function Highcharts.Chart#setResponsive
  45356. * @param {boolean} [redraw=true]
  45357. * @param {boolean} [reset=false]
  45358. * Reset by un-applying all rules. Chart.update resets all rules before
  45359. * applying updated options.
  45360. */
  45361. function setResponsive(redraw, reset) {
  45362. const options = this.options.responsive, currentResponsive = this.currentResponsive;
  45363. let ruleIds = [], undoOptions;
  45364. if (!reset && options && options.rules) {
  45365. options.rules.forEach((rule) => {
  45366. if (typeof rule._id === 'undefined') {
  45367. rule._id = uniqueKey();
  45368. }
  45369. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  45370. }, this);
  45371. }
  45372. // Merge matching rules
  45373. const mergedOptions = merge(...ruleIds
  45374. .map((ruleId) => find((options || {}).rules || [], (rule) => (rule._id === ruleId)))
  45375. .map((rule) => (rule && rule.chartOptions)));
  45376. mergedOptions.isResponsiveOptions = true;
  45377. // Stringified key for the rules that currently apply.
  45378. ruleIds = (ruleIds.toString() || void 0);
  45379. const currentRuleIds = (currentResponsive && currentResponsive.ruleIds);
  45380. // Changes in what rules apply
  45381. if (ruleIds !== currentRuleIds) {
  45382. // Undo previous rules. Before we apply a new set of rules, we
  45383. // need to roll back completely to base options (#6291).
  45384. if (currentResponsive) {
  45385. this.update(currentResponsive.undoOptions, redraw, true);
  45386. }
  45387. if (ruleIds) {
  45388. // Get undo-options for matching rules. The `undoOptions``
  45389. // hold the current values before they are changed by the
  45390. // `mergedOptions`.
  45391. undoOptions = diffObjects(mergedOptions, this.options, true, this.collectionsWithUpdate);
  45392. undoOptions.isResponsiveOptions = true;
  45393. this.currentResponsive = {
  45394. ruleIds: ruleIds,
  45395. mergedOptions: mergedOptions,
  45396. undoOptions: undoOptions
  45397. };
  45398. this.update(mergedOptions, redraw, true);
  45399. }
  45400. else {
  45401. this.currentResponsive = void 0;
  45402. }
  45403. }
  45404. }
  45405. })(Responsive || (Responsive = {}));
  45406. /* *
  45407. *
  45408. * Default Export
  45409. *
  45410. * */
  45411. /* *
  45412. *
  45413. * API Declarations
  45414. *
  45415. * */
  45416. /**
  45417. * A callback function to gain complete control on when the responsive rule
  45418. * applies.
  45419. *
  45420. * @callback Highcharts.ResponsiveCallbackFunction
  45421. *
  45422. * @param {Highcharts.Chart} this
  45423. * Chart context.
  45424. *
  45425. * @return {boolean}
  45426. * Return `true` if it applies.
  45427. */
  45428. (''); // keeps doclets above in JS file
  45429. /* *
  45430. *
  45431. * API Options
  45432. *
  45433. * */
  45434. /**
  45435. * Allows setting a set of rules to apply for different screen or chart
  45436. * sizes. Each rule specifies additional chart options.
  45437. *
  45438. * @sample {highstock} stock/demo/responsive/
  45439. * Stock chart
  45440. * @sample highcharts/responsive/axis/
  45441. * Axis
  45442. * @sample highcharts/responsive/legend/
  45443. * Legend
  45444. * @sample highcharts/responsive/classname/
  45445. * Class name
  45446. *
  45447. * @since 5.0.0
  45448. * @apioption responsive
  45449. */
  45450. /**
  45451. * A set of rules for responsive settings. The rules are executed from
  45452. * the top down.
  45453. *
  45454. * @sample {highcharts} highcharts/responsive/axis/
  45455. * Axis changes
  45456. * @sample {highstock} highcharts/responsive/axis/
  45457. * Axis changes
  45458. * @sample {highmaps} highcharts/responsive/axis/
  45459. * Axis changes
  45460. *
  45461. * @type {Array<*>}
  45462. * @since 5.0.0
  45463. * @apioption responsive.rules
  45464. */
  45465. /**
  45466. * A full set of chart options to apply as overrides to the general
  45467. * chart options. The chart options are applied when the given rule
  45468. * is active.
  45469. *
  45470. * A special case is configuration objects that take arrays, for example
  45471. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  45472. * collections, an `id` option is used to map the new option set to
  45473. * an existing object. If an existing object of the same id is not found,
  45474. * the item of the same indexupdated. So for example, setting `chartOptions`
  45475. * with two series items without an `id`, will cause the existing chart's
  45476. * two series to be updated with respective options.
  45477. *
  45478. * @sample {highstock} stock/demo/responsive/
  45479. * Stock chart
  45480. * @sample highcharts/responsive/axis/
  45481. * Axis
  45482. * @sample highcharts/responsive/legend/
  45483. * Legend
  45484. * @sample highcharts/responsive/classname/
  45485. * Class name
  45486. *
  45487. * @type {Highcharts.Options}
  45488. * @since 5.0.0
  45489. * @apioption responsive.rules.chartOptions
  45490. */
  45491. /**
  45492. * Under which conditions the rule applies.
  45493. *
  45494. * @since 5.0.0
  45495. * @apioption responsive.rules.condition
  45496. */
  45497. /**
  45498. * A callback function to gain complete control on when the responsive
  45499. * rule applies. Return `true` if it applies. This opens for checking
  45500. * against other metrics than the chart size, for example the document
  45501. * size or other elements.
  45502. *
  45503. * @type {Highcharts.ResponsiveCallbackFunction}
  45504. * @since 5.0.0
  45505. * @context Highcharts.Chart
  45506. * @apioption responsive.rules.condition.callback
  45507. */
  45508. /**
  45509. * The responsive rule applies if the chart height is less than this.
  45510. *
  45511. * @type {number}
  45512. * @since 5.0.0
  45513. * @apioption responsive.rules.condition.maxHeight
  45514. */
  45515. /**
  45516. * The responsive rule applies if the chart width is less than this.
  45517. *
  45518. * @sample highcharts/responsive/axis/
  45519. * Max width is 500
  45520. *
  45521. * @type {number}
  45522. * @since 5.0.0
  45523. * @apioption responsive.rules.condition.maxWidth
  45524. */
  45525. /**
  45526. * The responsive rule applies if the chart height is greater than this.
  45527. *
  45528. * @type {number}
  45529. * @default 0
  45530. * @since 5.0.0
  45531. * @apioption responsive.rules.condition.minHeight
  45532. */
  45533. /**
  45534. * The responsive rule applies if the chart width is greater than this.
  45535. *
  45536. * @type {number}
  45537. * @default 0
  45538. * @since 5.0.0
  45539. * @apioption responsive.rules.condition.minWidth
  45540. */
  45541. (''); // keeps doclets above in JS file
  45542. return Responsive;
  45543. });
  45544. _registerModule(_modules, 'masters/highcharts.src.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Core/Defaults.js'], _modules['Core/Animation/Fx.js'], _modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Renderer/HTML/AST.js'], _modules['Core/Templating.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Renderer/HTML/HTMLElement.js'], _modules['Core/Renderer/HTML/HTMLRenderer.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/Axis/LogarithmicAxis.js'], _modules['Core/Axis/PlotLineOrBand/PlotLineOrBand.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Tooltip.js'], _modules['Core/Series/Point.js'], _modules['Core/Pointer.js'], _modules['Core/Legend/Legend.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Axis/Stacking/StackingAxis.js'], _modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Series/Column/ColumnSeries.js'], _modules['Series/Column/ColumnDataLabel.js'], _modules['Series/Pie/PieSeries.js'], _modules['Series/Pie/PieDataLabel.js'], _modules['Core/Series/DataLabel.js'], _modules['Core/Responsive.js'], _modules['Core/Color/Color.js'], _modules['Core/Time.js']], function (Highcharts, Utilities, Defaults, Fx, Animation, AST, Templating, RendererUtilities, SVGElement, SVGRenderer, HTMLElement, HTMLRenderer, Axis, DateTimeAxis, LogarithmicAxis, PlotLineOrBand, Tick, Tooltip, Point, Pointer, Legend, Chart, StackingAxis, StackItem, Series, SeriesRegistry, ColumnSeries, ColumnDataLabel, PieSeries, PieDataLabel, DataLabel, Responsive, Color, Time) {
  45545. const G = Highcharts;
  45546. // Animation
  45547. G.animate = Animation.animate;
  45548. G.animObject = Animation.animObject;
  45549. G.getDeferredAnimation = Animation.getDeferredAnimation;
  45550. G.setAnimation = Animation.setAnimation;
  45551. G.stop = Animation.stop;
  45552. G.timers = Fx.timers;
  45553. // Classes
  45554. G.AST = AST;
  45555. G.Axis = Axis;
  45556. G.Chart = Chart;
  45557. G.chart = Chart.chart;
  45558. G.Fx = Fx;
  45559. G.Legend = Legend;
  45560. G.PlotLineOrBand = PlotLineOrBand;
  45561. G.Point = Point;
  45562. G.Pointer = Pointer;
  45563. G.Series = Series;
  45564. G.StackItem = StackItem;
  45565. G.SVGElement = SVGElement;
  45566. G.SVGRenderer = SVGRenderer;
  45567. G.Templating = Templating;
  45568. G.Tick = Tick;
  45569. G.Time = Time;
  45570. G.Tooltip = Tooltip;
  45571. // Color
  45572. G.Color = Color;
  45573. G.color = Color.parse;
  45574. // Compositions
  45575. HTMLRenderer.compose(SVGRenderer);
  45576. HTMLElement.compose(SVGElement);
  45577. Pointer.compose(Chart);
  45578. Legend.compose(Chart);
  45579. // DefaultOptions
  45580. G.defaultOptions = Defaults.defaultOptions;
  45581. G.getOptions = Defaults.getOptions;
  45582. G.time = Defaults.defaultTime;
  45583. G.setOptions = Defaults.setOptions;
  45584. // Format Utilities
  45585. G.dateFormat = Templating.dateFormat;
  45586. G.format = Templating.format;
  45587. G.numberFormat = Templating.numberFormat;
  45588. // Utilities
  45589. G.addEvent = Utilities.addEvent;
  45590. G.arrayMax = Utilities.arrayMax;
  45591. G.arrayMin = Utilities.arrayMin;
  45592. G.attr = Utilities.attr;
  45593. G.clearTimeout = Utilities.clearTimeout;
  45594. G.correctFloat = Utilities.correctFloat;
  45595. G.createElement = Utilities.createElement;
  45596. G.css = Utilities.css;
  45597. G.defined = Utilities.defined;
  45598. G.destroyObjectProperties = Utilities.destroyObjectProperties;
  45599. G.discardElement = Utilities.discardElement;
  45600. G.distribute = RendererUtilities.distribute;
  45601. G.erase = Utilities.erase;
  45602. G.error = Utilities.error;
  45603. G.extend = Utilities.extend;
  45604. G.extendClass = Utilities.extendClass;
  45605. G.find = Utilities.find;
  45606. G.fireEvent = Utilities.fireEvent;
  45607. G.getMagnitude = Utilities.getMagnitude;
  45608. G.getStyle = Utilities.getStyle;
  45609. G.inArray = Utilities.inArray;
  45610. G.isArray = Utilities.isArray;
  45611. G.isClass = Utilities.isClass;
  45612. G.isDOMElement = Utilities.isDOMElement;
  45613. G.isFunction = Utilities.isFunction;
  45614. G.isNumber = Utilities.isNumber;
  45615. G.isObject = Utilities.isObject;
  45616. G.isString = Utilities.isString;
  45617. G.keys = Utilities.keys;
  45618. G.merge = Utilities.merge;
  45619. G.normalizeTickInterval = Utilities.normalizeTickInterval;
  45620. G.objectEach = Utilities.objectEach;
  45621. G.offset = Utilities.offset;
  45622. G.pad = Utilities.pad;
  45623. G.pick = Utilities.pick;
  45624. G.pInt = Utilities.pInt;
  45625. G.relativeLength = Utilities.relativeLength;
  45626. G.removeEvent = Utilities.removeEvent;
  45627. G.seriesType = SeriesRegistry.seriesType;
  45628. G.splat = Utilities.splat;
  45629. G.stableSort = Utilities.stableSort;
  45630. G.syncTimeout = Utilities.syncTimeout;
  45631. G.timeUnits = Utilities.timeUnits;
  45632. G.uniqueKey = Utilities.uniqueKey;
  45633. G.useSerialIds = Utilities.useSerialIds;
  45634. G.wrap = Utilities.wrap;
  45635. // Compositions
  45636. ColumnDataLabel.compose(ColumnSeries);
  45637. DataLabel.compose(Series);
  45638. DateTimeAxis.compose(Axis);
  45639. LogarithmicAxis.compose(Axis);
  45640. PieDataLabel.compose(PieSeries);
  45641. PlotLineOrBand.compose(Axis);
  45642. Responsive.compose(Chart);
  45643. StackingAxis.compose(Axis, Chart, Series);
  45644. Tooltip.compose(Pointer);
  45645. // Default Export
  45646. return G;
  45647. });
  45648. _modules['masters/highcharts.src.js']._modules = _modules;
  45649. return _modules['masters/highcharts.src.js'];
  45650. }));